Compare commits

...

415 commits

Author SHA1 Message Date
ergo720
dd36dd598c
Merge pull request #2484 from ergo720/update_sdl
Updated SDL submodule to version 2.30.11
2025-03-31 13:08:49 +02:00
ergo720
87634a2e27 Updated SDL submodule to version 2.30.11 2025-03-30 10:27:23 +02:00
Luke Usher
6f32d89545
Merge pull request #2474 from Margen67/build
cmake: Replace /Ob2 with /Ob3
2024-12-23 08:55:15 +00:00
Margen67
ec0c288bc4 cmake: Replace /Ob2 with /Ob3
See https://learn.microsoft.com/en-us/cpp/build/reference/ob-inline-function-expansion
2024-12-17 00:34:09 -08:00
RadWolfie
8bfbcb56fd
Merge pull request #2473 from Margen67/w11
Disable rounded corners on Windows 11
2024-12-17 01:59:01 -06:00
Margen67
8965d2443b Remove rounded corners on Windows 11 2024-12-16 23:48:10 -08:00
RadWolfie
b33ed95c5b
Merge pull request #2472 from Margen67/ci2
CI: Upgrade actions
2024-12-16 22:15:06 -06:00
Margen67
8ee17b512c CI: Update actions 2024-12-16 19:39:48 -08:00
RadWolfie
50334cbc31
Merge pull request #2469 from Margen67/subhook
Replace subhook with working mirror
2024-12-13 14:52:09 -06:00
Margen67
41454b8c26 Replace subhook with working mirror 2024-12-13 12:41:38 -08:00
Luke Usher
204dcf8801
Merge pull request #2462 from RadWolfie/file-minor-fixes
File minor fixes
2024-08-25 10:21:29 +01:00
RadWolfie
77c63ceec3 kernel: fix dashupdate titles attempt delete new files
NOTE: Partition2.bin needs to be emulated in order for copy files to partition and fatx metadata.
2024-08-18 08:39:16 -05:00
RadWolfie
2cfaba893e kernel: fix Exhibition Demo discs problem for copy soundtracks onto hdd (require force santion) 2024-08-18 08:39:16 -05:00
Luke Usher
17b0cb81d4 CI: Specify minimum platform and SDK version 2024-07-08 15:19:54 +01:00
RadWolfie
daa6a816ff Merge experimental chihiro branch 2024-07-05 12:19:54 -05:00
Luke Usher
6caf3ea679 chihiro: prevent JVS register updates from being missed due to long delays
This really needs a better solution, but for now, this will do.
2024-07-05 12:19:54 -05:00
Luke Usher
9a58823b70 chihiro: emulate a chihiro system when boot.id is present 2024-07-05 12:19:54 -05:00
Luke Usher
3edd8d168b chihiro: fix an issue where media board detection failed due to instant response time 2024-07-05 12:19:54 -05:00
RadWolfie
f894d31332 Cleanly rebase chihiro-work on develop
Co-authored-by: Luke Usher <luke.usher@outlook.com>
Co-authored-by: wutno <aaron@installgentoo.net>
Co-authored-by: RadWolfie <RadWolfie@users.noreply.github.com>
2024-07-05 12:19:54 -05:00
RadWolfie
9241bec768 Merge ergo720 less_busy_loops branch 2024-07-05 12:19:53 -05:00
RadWolfie
c50a0c5c7d Merge EmuX86 passive branch 2024-07-05 12:18:57 -05:00
Luke Usher
87bab04932 EmuX86: Let invalid memory accesses trigger a warning rather than a fatal error
This seems to resolve most regressions we have had in recent history.
2024-07-05 12:18:54 -05:00
ergo720
ad6769bbf3 Never change the thread priority on the host and the disable boost flag too
This fixes almost all the games that were broken in this branch
2024-07-05 10:58:05 -05:00
ergo720
0e63131fc3 Use a DPC for expired timers + don't execute NV2A DPCs from the timer thread to avoid the exception overhead 2024-07-05 10:58:05 -05:00
ergo720
889040c56a Fixed an issue in WaitApc where the wait block was not removed when using a zero timeout or when satisfied by a user APC + properly lock the wait block operations to avoid a race between SatisfyWait and KiTimerExpiration 2024-07-05 10:58:05 -05:00
ergo720
86542c9f2e Implemented PTIMER alarm interrupt of NV2A + fixed a bug in timer_init
This fixes DOAU showing the dirty disk error in PAL50 mode
2024-07-05 10:58:05 -05:00
ergo720
c9edbd1003 Fixed wrong nv2a clock frequency
This is accessed by DOAU via PTIMER only in PAL50 mode
2024-07-05 10:58:05 -05:00
ergo720
ebb122f2a0 Fixed a bug in KeTickCount + check all timer indices when we are late in KiClockIsr
This almost completely fixes the slowness in Panzer Dragoon Orta
2024-07-05 10:58:04 -05:00
ergo720
c158a472ff Make sure to reset WaitStatus when a new wait starts
This fixes an issue in Panzer Dragoon Orta, where KeDelayExecutionThread would return X_STATUS_TIMEOUT | X_STATUS_USER_APC
2024-07-05 10:58:04 -05:00
ergo720
6961d1c7a1 Make sure that GetNativeHandle succeeds before attempting to get the native handle
This fixes a sporadic crash in Panzer Dragoon Orta, where the title calls KeSetBasePriorityThread on a thread that has already terminated
2024-07-05 10:58:04 -05:00
ergo720
2f7cfe7e95 Fixed a bug in KiInsertTimerTable + log all objects being waited on in NtWaitForMultipleObjectsEx
This fixes a crash in Metal Slug 3
2024-07-05 10:58:04 -05:00
ergo720
46d0173673 Account for partial milliseconds in KiClockIsr
This fixes the slowness in The lord of the rings: the third era
2024-07-05 10:58:04 -05:00
ergo720
c7b028b3e7 Fixed a race condition in WaitApc + removed wrong InsertTailList for ktimers used during a timeout
This fixes almost all broken games in this branch. Still broken: PDO: 1 fps vs 10 fps, DOA3: freezes after title screen, Lord of the rings The third era: Unable to determine default Xbox backbuffer error???
2024-07-05 10:58:04 -05:00
ergo720
3d12edc77d Always create a wait object even when we satisfy the wait on the host side + fixed a bug in KiWaitTestNoYield
This fixes an occasionl freeze in Steel Battalion + the slowness in JSRF
2024-07-05 10:58:04 -05:00
ergo720
08ab4b9164 Revert to using the host to do thread suspension 2024-07-05 10:58:04 -05:00
ergo720
4fca5c7007 Hack: <= thread priority instead of >= 2024-07-05 10:58:04 -05:00
ergo720
e26f20108a Setup a KTIMER for the other functions using WaitApc too 2024-07-05 10:58:04 -05:00
ergo720
8475124e5b Restore single interrupt loop in update_non_periodic_events 2024-07-05 10:58:03 -05:00
ergo720
9b2ae106e5 Place nvnet in its own thread 2024-07-05 10:58:03 -05:00
ergo720
b3bfeca3a8 Use get_now directly in system_events instead of qpc 2024-07-05 10:58:03 -05:00
ergo720
b77a13b708 Adjust KeSystemTime when the host system time is changed by the user 2024-07-05 10:58:03 -05:00
ergo720
1b5e111ae3 Account for delays between calls to KiClockIsr
This fixes the slowness in the dashboard
2024-07-05 10:58:03 -05:00
ergo720
1504a75a46 Raise priority of system events thread 2024-07-05 10:58:03 -05:00
ergo720
87496ab873 Removed delta amount added to KeSystemTime 2024-07-05 10:58:03 -05:00
ergo720
5b37a7ec21 Fixed thread order initialization when a thread starts suspended 2024-07-05 10:58:03 -05:00
ergo720
639f42c318 Make sure to hold the DPC lock until the DPC list has been emptied
This fixes a crash in Lord of the rings: The fellowship of the ring
2024-07-05 10:58:03 -05:00
ergo720
8d92992a6b Implemented kernel unwait routines + updated/fixed KeWaitForMultipleObjects and KeWaitForSingleObject 2024-07-05 10:58:02 -05:00
ergo720
7323eed73e Only change the priority of a thread if it is being set above normal 2024-07-05 10:58:02 -05:00
ergo720
b47c1f195c Unpatch D3DDevice_BlockUntilVerticalBlank and D3DDevice_SetVerticalBlankCallback 2024-07-05 10:58:02 -05:00
ergo720
7c73bfc525 Avoid triggering multiple gpu interrupts outside the vblank 2024-07-05 10:58:02 -05:00
ergo720
1b4a3bb54f Moved position of ObfDereferenceObject in NtSuspendThread 2024-07-05 10:58:02 -05:00
ergo720
750d202fa8 Removed scaling hack in KeInterruptTime and KeTickCount + added yield in system_events routine
This fixes the stuttering in Halo 2, Metal slug 3, JSRF and restores PDO, PSO to the same state as in master
2024-07-05 10:58:02 -05:00
ergo720
e7bca5e1bf Implemented suspend/resume kernel Nt routines with the corresponding Ke routines 2024-07-05 10:58:01 -05:00
ergo720
937ab9e1c2 Fixed a bug in KeSetBasePriorityThread 2024-07-05 10:58:01 -05:00
ergo720
8006f55cf3 Merge many different periodic events in a single thread, instead of each having its own busy loop
This merges vblank, ohci's eof, pit interrupt, dsound sync and async workers, nvnet packet processing and system interrupt
2024-07-05 10:58:01 -05:00
ergo720
1828ddfd6f Merge lle and hle vblank routines in a single thread 2024-07-05 10:58:00 -05:00
ergo720
bc42cfaa6b Removed unnecessary lock in the interrupt thread 2024-07-05 10:58:00 -05:00
PatrickvL
b1235b7733 Merge pull request #2458 from LukeUsher/fix-compilation-vs2022 2024-07-05 10:41:18 -05:00
Luke Usher
1615ecc976 fix the build on vs2022 17.9.1 2024-05-22 12:46:03 +01:00
Luke Usher
0007d20b03
Merge pull request #2445 from medievil1/fog-stuff
fog stuff
2024-05-22 12:42:07 +01:00
RadWolfie
bfb10092c0
Merge pull request #2451 from RadWolfie/fix-hardware-model
Fix hardware model conversions + use respective hardware model based on console type
2024-02-08 12:48:46 -06:00
Luke Usher
f5b4878245
Merge pull request #2452 from medievil1/my-master
fix incorrect setting in pixel shader template
2024-02-04 19:47:12 +00:00
RadWolfie
4174fbc23f review remarks 2024-02-04 13:29:40 -06:00
medievil1
0560ed6955 change clamp to saturate in shader templates
per discussions
2024-02-03 14:46:54 -05:00
medievil1
875015164c fix incorrect setting in pixel shader template
it should be normal2, not normal 3...
PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) works in conjunction with #define PS_TEXTUREMODES_DOT_RFLCT_SPEC(ts)(after) and PS_TEXTUREMODES_DOTPRODUCT(ts)(before)
dotproduct uses it's own product/normal, dot_rflct uses dotproduct and it's own, and dot_spec uses the previous 2 and it's own ... this corrects light reflection on the floor in Halo
2024-02-03 14:38:16 -05:00
medievil1
aedb5ba87b address review comment
remove PB pr sampling adjustments
2024-02-03 12:08:00 -05:00
RadWolfie
e5dcdebe7f device: corrected conversions based on hardware model 2024-02-03 02:45:37 -06:00
medievil1
ad0b8340da change flow of fog table/enable
per Jack, change flow to :
if fog is disabled, avoid table code and just set fogFactor to 1
2024-01-24 00:34:47 -05:00
medievil1
7298b6c4dc address review comment
fix inadvertently not changing pixel shader pass to template c reg info
2024-01-23 21:45:15 -05:00
medievil1
b20db36e15 fix template formatting
hopefully it all matches now

Revert "fix template formatting"

This reverts commit 79aa4436ef330e8754755044ce3ebf9549c557ae.

fix template formatting

hopefully it all matches now
3rd times a charm  hopefully...lol
2024-01-21 14:52:36 -05:00
medievil1
a03d50df56 add FF fog move to Pixelshader 2024-01-20 15:14:02 -05:00
medievil1
0e25897f77 address review comments 2024-01-19 00:56:03 -05:00
medievil1
c6049b768c address review comments 2024-01-18 09:30:07 -05:00
medievil1
399baccadb fog stuff
fix no fog issue which was incorrectly passing data into iFog instead of 1 when fog was disabled

testing a move to pixel shader

adjust passthrough template

for move to pixel shader

remove printf
2024-01-18 01:29:15 -05:00
ergo720
3edc388abf
Merge pull request #2444 from RadWolfie/update-time-api
Fix RtlTimeFieldsToTime and RtlTimeToTimeFields implements
2024-01-15 15:38:03 +01:00
RadWolfie
796e8d2beb kernel: change 1000 to MSECSPERSEC 2024-01-15 05:19:57 -06:00
RadWolfie
282c5f5622 kernel: RtlTimeToTimeFields no longer need to be logged 2024-01-13 16:25:58 -06:00
RadWolfie
8e5b27d054 kernel: fix RtlTimeToTimeFields implement to match test results 2024-01-13 16:22:05 -06:00
RadWolfie
b64a3b6faa kernel: fix comment typo in RtlTimeToTimeFields 2024-01-13 16:18:23 -06:00
RadWolfie
d64e172c9f kernel: RtlTimeFieldsToTime no longer need to be logged 2024-01-05 01:26:08 -06:00
RadWolfie
d6b96b8ea1 kernel: clean up RtlTimeFieldsToTime bad indents 2024-01-05 01:19:10 -06:00
RadWolfie
131b330a85 kernel: Change RtlTimeFieldsToTime's Time format into more readable math operation 2024-01-05 01:17:43 -06:00
RadWolfie
bf6193202a kernel: split RtlTimeFieldsToTime range check into their own if statements for clear reading
Plus fixed a bug for leap year's day range
2024-01-05 01:15:15 -06:00
RadWolfie
06c28a847e Revert "Merge pull request #2441 from ergo720/time_fix"
This reverts commit 8cc9c73f58.
2024-01-05 00:33:34 -06:00
PatrickvL
8cc9c73f58
Merge pull request #2441 from ergo720/time_fix 2023-12-30 09:53:52 +01:00
ergo720
2452965580 Fixed a bug in RtlTimeFieldsToTime 2023-12-29 14:00:15 +01:00
PatrickvL
eddc14e151
Merge pull request #2439 from NZJenkins/hotload_shaders 2023-12-29 11:43:42 +01:00
Luke Usher
31ff15ba1d
Merge pull request #2440 from RadWolfie/fix-xkts-issues
Fix Xbox Kernel Test Suite issues discovered
2023-12-21 15:29:44 +00:00
RadWolfie
4d9151ca26 rtl: remove unnecessary double setter in RtlInitUnicodeString 2023-12-20 09:31:35 -06:00
RadWolfie
1f1d1ac631 rtl: fix RtlCompareString and RtlCompareUnicodeString to match with kernel test suite 2023-12-17 20:50:46 -06:00
RadWolfie
e5043dbc05 fix UNICODE_STRING's Buffer variable type issue 2023-12-17 20:49:10 -06:00
RadWolfie
a2fb41856d kernel: fix RtlWalkFrameChain according to xbox kernel test suite failed test 2023-12-17 19:02:20 -06:00
RadWolfie
b09d3ca69a kernel: update RtlAnsiStringToUnicodeString to include error log returns 2023-12-17 11:41:12 -06:00
Anthony
684d3338f2 Always copy hlsl files if they changed in the sources
Require files to be explicitly declared in CXBXR_HEADER_EMU in order to be copied
2023-11-18 11:34:05 +13:00
Anthony
ae140bb6bf Copy and install hlsl files in the cxbx project 2023-11-16 23:00:18 +13:00
Anthony
a5b8f15a14 reformat todo 2023-11-12 20:04:39 +13:00
Anthony
2c8a764fc7 Save a backup copy of hlsl files 2023-11-12 19:56:20 +13:00
Anthony
605271245c review comments 2023-11-11 22:19:46 +13:00
Anthony
93e36f7be3 Rename shaderhlsl to shadersources
and updateshaders to update
2023-11-11 01:08:34 +13:00
Anthony
a7bc6a307d fixup remove unused variable 2023-11-11 01:08:34 +13:00
Anthony
7ad047bcea reduce crashing if the shaders are broken and get hotloaded 2023-11-11 01:08:34 +13:00
Anthony
0f21e25d7d Support hotloading pixelshaders 2023-11-11 01:08:34 +13:00
Anthony
79884bdf3d Move shader hlsl management into Shader.cpp
- g_ShaderHlsl keeps track of hlsl
- VS and PS source their hlsl from g_ShaderHlsl
2023-11-11 01:08:34 +13:00
Anthony
260e2fb7c8 Ensure filewatcher is closed if something goes wrong
and avoid creating multiple watchers
2023-11-11 01:08:34 +13:00
Anthony
4d221c3c81 tidy vertex shader loading 2023-11-11 01:08:34 +13:00
Anthony
712d3bee2f move passthrough shader to a file 2023-11-11 01:08:34 +13:00
Anthony
3cd551d827 Reload/recompile vertex shaders hlsl if they change 2023-11-11 01:08:34 +13:00
Anthony
397f33143d Rename VertexShaderSource to VertexShaderCache
because it was a weird name
2023-11-11 01:01:04 +13:00
ergo720
c7e75d7c5c
Merge pull request #2432 from Margen67/ci
CI: Upgrade checkout to v4
2023-10-21 16:19:43 +02:00
NZJenkins
4808be65c4
Slightly reduce build time (#2437) 2023-10-16 08:51:56 +02:00
Margen67
def10ff466
CI: Upgrade checkout to v4 2023-09-20 01:28:42 -07:00
Luke Usher
e1ea10c4cb
Merge pull request #2428 from jackchentwkh/fix_pushbuffer_subroutine
fix COMMAND_TYPE_CALL pushbuffer command handling
2023-09-03 21:45:52 +01:00
Jack Chen
67f21d5c30 reset subr_active flag to indicate we've returned from any COMMAND_TYPE_CALL command.
NV2A used COMMAND_TYPE_JUMP_LONG to return from COMMAND_TYPE_CALL.
Otogi uses lot's of COMMAND_TYPE_CALL, this should inprove the pushbuffer handling.
2023-09-03 20:42:07 +08:00
PatrickvL
971318a89a
Merge pull request #2426 from Margen67/ci
CI: Fix output
2023-07-27 13:56:36 +02:00
Margen67
b62d39da7d CI: Fix output
set-output is deprecated: https://github.blog/changelog/2023-07-24-github-actions-update-on-save-state-and-set-output-commands/
2023-07-25 16:46:37 -07:00
Luke Usher
6c530fbf86
Merge pull request #2418 from RadWolfie/fix-reboot-non-ansii-path
kernel: fix non-ansii file path conversion for reboot process
2023-03-13 12:41:17 +00:00
RadWolfie
a8f6d0496e kernel: fix non-ansii file path conversion for reboot process 2023-03-08 18:17:07 -06:00
Luke Usher
6389cb6524
Merge pull request #2417 from RadWolfie/d3d-update
Update XbSymbolDatabase fix and add LTCG patch missing
2023-03-06 14:50:23 +00:00
RadWolfie
ef3439e46f d3d8: fix GTA: SA bug and add LTCG patch for D3DDevice_DrawVertices variant
Plus other variant LTCG patches that only does logging. And symbol renames.
2023-03-06 05:47:39 -06:00
ergo720
ed8a6124e4
Merge pull request #2416 from ergo720/dpc_recursion_fix
Dpc recursion fix
2023-03-02 23:32:30 +01:00
ergo720
b1bd9dd5d0
Merge pull request #2414 from RadWolfie/fix-hacked-ob-handle-return
kernel: fix hacked windows handle check to bypass special handle of current process
2023-03-02 23:04:40 +01:00
ergo720
062752e1a7 Review remarks 2023-03-02 22:53:18 +01:00
ergo720
99ab34ac82 Fixed dpc recursion bug 2023-03-02 14:59:10 +01:00
RadWolfie
8e0df988a8 kernel: fix hacked windows handle check to bypass special handle of current process 2023-03-01 17:04:42 -06:00
ergo720
58041c95b4 Clean up unused dpc event member variable 2023-03-01 22:43:42 +01:00
RadWolfie
827a3212f8
Merge pull request #2413 from ergo720/priority_thread
Fixed a bug in KeSetDisableBoostThread
2023-03-01 10:14:34 -06:00
ergo720
d0890d588d Fixed a bug in KeSetDisableBoostThread 2023-03-01 17:10:24 +01:00
Luke Usher
9e9d3f390f
Merge pull request #2412 from ergo720/priority_thread
Fix a priority bug in KeQueryBasePriorityThread and KeSetBasePriorityThread
2023-03-01 16:04:57 +00:00
ergo720
4821a72b6f Set/query the priority of the requested thread, instead of the current one in KeQueryBasePriorityThread and KeSetBasePriorityThread 2023-03-01 16:48:30 +01:00
Luke Usher
bf1483ae56
Merge pull request #2411 from RadWolfie/update-xbsdb
lib: sync XbSymbolDatabase
2023-03-01 09:15:15 +00:00
RadWolfie
b2f05b8b0b lib: sync XbSymbolDatabase 2023-02-27 04:37:02 -06:00
ergo720
111728f170
Merge pull request #2408 from LukeUsher/avoid-region-patching
cxbxkrnl: avoid region patching loaded titles
2023-02-10 18:35:28 +01:00
ergo720
ce05ea1397
Merge pull request #2409 from RadWolfie/fix-emulation-status
Fix false positive emulation is either still running or stopped
2023-02-10 18:17:12 +01:00
RadWolfie
f4488c0270 fix false positive emulation is either still running or stopped 2023-02-10 07:16:12 -06:00
Luke Usher
65a5ad6591 cxbxkrnl: avoid region patching loaded titles
While it sounds ideal from a UX standpoint, region patching does break a number of
titles that would otherwise work.

Instead, show a warning that it may not be compatible with instructions on how to
configure region settings in eeprom.

Allow the user to attempt to run the title anyway, if the game does not do it's own
region checking, it will most likely just work regardless.
2023-02-07 22:08:31 +00:00
RadWolfie
0b695637ce
Merge pull request #2406 from LukeUsher/fix-xbe-version-reporting
xbe: fix version number formatting
2022-11-20 06:24:51 -06:00
Luke Usher
fe9a706a8e xbe: fix version number formatting 2022-11-19 16:06:12 +00:00
Luke Usher
8ac5d14cd2
Merge pull request #2401 from medievil1/new-master
correct hemisphere formula
2022-10-13 14:39:59 +01:00
Luke Usher
628323218a
Merge pull request #2404 from CookiePLMonster/remove-int32x32To64
Remove Int32x32To64 from the code
2022-10-06 22:41:51 +01:00
Silent
f7c09ddc4f
Remove Int32x32To64 as it's potentially harmful 2022-10-06 23:07:37 +02:00
ergo720
09e744ecc4
Merge pull request #2403 from RadWolfie/libusb-fixes
HOTFIX: Fix LibusbDevice's initialization process
2022-10-06 00:36:31 +02:00
RadWolfie
a37124e2dc readme: create a link to libusb's driver installation section to find suggested driver 2022-10-05 17:09:17 -05:00
RadWolfie
5f58ae918c input: have libusb_claim_interface's return actually give err number than comparsion check for non-zero 2022-10-05 16:46:17 -05:00
RadWolfie
f17b7f7fa6 input: check for error from libusb_open and report as device invalid 2022-10-05 16:40:39 -05:00
RadWolfie
4dccf6d5b9 input: don't override device's vendor id, expected to do comparsion instead 2022-10-05 06:42:56 -05:00
medievil1
6bbe6cefe8 added notes
changed a couple things around and added notes

correct hemisphere formula

correct one entry

make twointoone shorter

Per PatrickvL's suggestion and code
2022-09-25 19:32:25 -04:00
PatrickvL
547c3ae663
Merge pull request #2398 from LukeUsher/xxh3-hash 2022-09-24 07:26:34 +02:00
PatrickvL
c594e34ac5
Merge pull request #2400 from jarupxx/dialog 2022-09-24 07:24:38 +02:00
jarupxx
caae99952c Fixed a Folder select dialog 2022-09-24 04:51:55 +09:00
Luke Usher
aeeb67dc6a hasher: use xxh3 exclusively 2022-09-14 13:51:05 +01:00
Luke Usher
4204640052
Merge pull request #2397 from medievil1/new-master
correct mod instruction
2022-09-11 18:45:16 +01:00
medievil1
3c78dd29a7 correct mod instruction 2022-09-11 13:22:29 -04:00
PatrickvL
42ff76ce0f
Merge pull request #2389 from medievil1/upsteam-master 2022-08-26 22:26:56 +02:00
PatrickvL
bf931d2d81
Merge pull request #2393 from medievil1/new-master 2022-08-26 21:40:56 +02:00
medievil1
a2a4fb35a4 xdm, xdd, xmd component count fix
all 3 use 3 components but on cxbx they were using 4
discovered by NzJenkins
test case Morrowind water

cosmetic fix

:P

changed per Patrick's request

reinsert line accidentally deleted

final change, move xdot

needed one more cosmetic space change

more fornatting
2022-08-26 15:37:14 -04:00
medievil1
f570c05e95 fix up
REMOVE HACK comment
REMOVE extra white space
2022-07-17 23:58:16 -04:00
medievil1
0b76da7c00 per review remarls
moved the case to the else statement and added a log entry
2022-07-17 23:38:41 -04:00
medievil1
9ee5b45b88 typo fix 2022-07-17 10:16:27 -04:00
medievil1
ee7be21bbb fix dot_str_cube not kicking in
when a cube texture is used and dot_str_3d is the texture mode, it is suppose to be flagged by combiner for change to dot_str_cube.
2022-07-17 10:10:12 -04:00
Luke Usher
8fcc2f5e0c
Merge pull request #2387 from LukeUsher/fix-heap-corruption-at-startup-with-nvnet
Fix heap corruption in NVNetDevice::GetMacAddress
2022-07-13 09:36:31 +01:00
Luke Usher
56610cd899 Fix heap corruption in NVNetDevice::GetMacAddress
This could cause startup of CxbxR to fail for some users.
2022-07-12 09:40:27 +01:00
Luke Usher
9fab1d5bed
Merge pull request #2386 from LukeUsher/fix-multi-xbe-vfs
emufile: fix multi-xbe titles when running through xbox-iso-vfs
2022-07-11 14:22:49 +01:00
PatrickvL
0043e45531
Merge pull request #2374 from NZJenkins/vsh-op-independence
Fix vertex shader op independence
2022-07-08 22:18:50 +02:00
Luke Usher
bc9cbec518
Merge pull request #2385 from ergo720/gui_xbe_crash
Fixed a crash in the gui when opening an xbe fails
2022-07-08 20:40:53 +01:00
Luke Usher
4edd3feb3e emufile: fix multi-xbe titles when running through xbox-iso-vfs
Prior to this commit, the relaunch path would become corrupted when running a game mounted with xbox-iso-vfs.
2022-07-08 20:37:32 +01:00
ergo720
0717c0166b Fixed a crash in the gui when opening an xbe fails 2022-07-08 13:38:52 +02:00
PatrickvL
16ffe3a80f
Merge pull request #2384 from LukeUsher/allow-skipping-faulty-instructions
EmuException: allow skipping of instructions that trigger unhandled exceptions
2022-07-08 13:21:15 +02:00
Luke Usher
5f3cfdeb77 address feedback 2022-07-08 11:37:48 +01:00
Luke Usher
a650fd2078
Merge pull request #2375 from ergo720/libusb_update
Updated libusb submodule to include deadlock fix
2022-07-08 09:59:19 +01:00
Luke Usher
cfa7be71cf
Merge pull request #2378 from ergo720/sb_fix
Fixed slowness in Steel Battalion caused by WaitApc
2022-07-08 09:58:01 +01:00
Luke Usher
4076a5b758 EmuException: allow skipping of instructions that trigger unhandled exceptions.
In many cases, this will result in more instability, however, it is useful as a debugging tool:
some games that would otherwise be working are let down by a *single* invalid read or write, and skipping
over that instruction allows the game to be played.

This can enable further research/debugging within the title.
2022-07-06 09:21:23 +01:00
ergo720
6e3635d90a Updated libusb to include deadlock fix 2022-07-05 22:25:08 +02:00
ergo720
ce55fe8627 Fixed slowness in Steel Battalion caused by WaitApc 2022-07-05 22:24:12 +02:00
Luke Usher
b2f63918de
Merge pull request #2382 from jackchentwkh/vsh_cpu_2
Implement HLE D3DDevice_RunVertexStateShader()
2022-07-05 15:57:43 +01:00
Luke Usher
f6274cc59f Revert "fix order of thread initialization"
This reverts commit 3a50d7e136.
2022-07-05 15:29:55 +01:00
PatrickvL
8546d7c10d EMUPATCH(D3DDevice_RunVertexStateShader):
added LOG_TEST_CASE precondition checks, replacing an assert
simplified implementation (no need for an intermediate variable)
commented some notes and future suggestions
2022-06-30 11:33:29 +02:00
jackchentwkh
4c5995af0c using memset for vertex_state_shader_v0[] init. 2022-06-26 11:00:04 +08:00
jackchentwkh
c981ff23b1 Correction of v0.xyzw assignment. 2022-06-26 11:00:04 +08:00
jackchentwkh
186b5fa8ee adopt api changes of nv2a_vsh_emu_execute_track_context_writes().
using pg->vsh_constants_dirty[] again.
2022-06-26 11:00:04 +08:00
jackchentwkh
ce4f4a07f0 adopt api nv2a_vsh_emu_initialize_xss_execution_state change from 3 args to 2 args.
mark all vertex constants dirty after vertex state shader execution.
2022-06-26 11:00:04 +08:00
jackchentwkh
4d110bad6e Implement RunVertexStateShader() 2022-06-26 11:00:04 +08:00
RadWolfie
6f79b035bd cmake: include nv2a_vsh_cpu libraries 2022-06-26 11:00:03 +08:00
jackchentwkh
7bc95d7a67 Add nv2a_vsh_cpu submodule 2022-06-26 11:00:03 +08:00
NZJenkins
5a454aad5c
Merge pull request #2381 from NZJenkins/cubemap-mips
Fix mipmapped cubemaps
2022-06-25 17:49:31 +12:00
Anthony
6cbb385b89 Fix mipmapped cubemaps
which had broken faces (other than the first face)
because the slice pitch calculation did not account for mipmaps.
We are iterating the mipmaps already, so just calculate the slice pitch directly
rather than trying to generalize CxbxGetPixelContainerMeasures
2022-06-22 23:57:22 +12:00
RadWolfie
31a47cde37
Merge pull request #2377 from ergo720/suspend_disable
Fix shutdown instability
2022-06-20 16:10:37 -05:00
ergo720
aba8fc8341 Fix shutdown freeze 2022-06-20 22:24:11 +02:00
Anthony
46b1f24153 Fix vertex shader op independence
Ensure the MAC op does not interfere with the input of the ILU op.
- Use a temp register to hold the input of the ILU op when necessary
- Reorganize vertex shader decoding to better reflect the data.
Decode one vsh instruction to one intermediate instruction, rather than to multiple independent instructions.
Test case:
KOTOR II (menu)
GTA III (lighting)
2022-06-18 15:24:08 +12:00
PatrickvL
b43f6bbcdf
Merge pull request #2369 from RadWolfie/fix-vs-popup-newline-dialog
Fix Visual Studio's popup dialog about newline correction
2022-05-28 20:58:10 +02:00
RadWolfie
33aad02f93 fix Visual Studio's popup dialog about newline correction 2022-05-28 10:36:55 -05:00
Luke Usher
1710f01c35
Merge pull request #2364 from RadWolfie/update-subhook
Update subhook to restore support for chihiro research
2022-05-22 11:17:04 +01:00
RadWolfie
f8e4f59eae update subhook to restore support for chihiro research 2022-05-21 13:53:46 -05:00
RadWolfie
4b5edbdc94
Merge pull request #2358 from RadWolfie/update-overlay
Add build, title name, and file name to overlay
2022-05-21 13:48:56 -05:00
PatrickvL
3bf2effa4d
Merge pull request #2359 from RadWolfie/fix-faux-fs-toggle
Fix faux fullscreen overlay input
2022-05-21 19:45:06 +02:00
RadWolfie
6ab30793ed review remark 2022-05-21 12:43:24 -05:00
Luke Usher
3d244b78b3
Merge pull request #2363 from NZJenkins/setmodelview
Minimal SetModelView implementation
2022-05-21 13:31:07 +01:00
Anthony
2512840968 Minimal SetModelView implementation 2022-05-21 19:24:08 +12:00
RadWolfie
ecef7aec39 review remark 2022-05-16 11:40:32 -05:00
Luke Usher
95b789eb27
Merge pull request #2360 from CookiePLMonster/fix-sleep
Do not leave KeDelayExecutionThread prematurely
2022-05-16 09:43:04 +01:00
Luke Usher
5e928e508a
Merge pull request #2362 from CookiePLMonster/resource-creation-lifetime
Keep D3D resources alive during resource creation
2022-05-16 09:10:10 +01:00
Silent
39ced81d58
Keep D3D resources alive during creation to prevent them being destroyed too early
Fixes an issue where a game could tear down a resource from another
thread while it's still initializing
2022-05-15 23:11:08 +02:00
Silent
b7006e2b01
Do not leave KeDelayExecutionThread prematurely
Fixes games waking up from Sleep() calls immediately
2022-05-15 14:47:39 +02:00
RadWolfie
fa85d3dad4 fix faux fullscreen toggle 2022-05-10 19:07:45 -05:00
RadWolfie
05a7acf13e overlay: Add build, title name, and file name to overlay 2022-05-09 10:39:14 -05:00
RadWolfie
8b35389c71
Merge pull request #2357 from ergo720/sb_auto_cursor
Make the SBC use the cursor mouse mode by default
2022-05-08 17:50:38 -05:00
RadWolfie
e8f943ebbc
Merge pull request #2356 from ergo720/fix_irql_mask
Fixed wrong irql masks
2022-05-08 17:50:18 -05:00
ergo720
87042c6bcc Make the SBC use the cursor mouse mode by default 2022-05-08 14:17:11 +02:00
ergo720
88a37ac496
Merge pull request #2353 from RadWolfie/fix-tls-data-emu
Fix tls data initialization from cxbxr's emulation threads end
2022-05-08 01:34:07 +02:00
ergo720
2168b033c6 Added comment 2022-05-07 22:30:35 +02:00
ergo720
a1cffc79f8 Fixed wrong irql masks 2022-05-07 18:56:33 +02:00
RadWolfie
1077115038
Merge pull request #2355 from RadWolfie/fix-gui-purge-regress
Fix pull request 2348 regression
2022-05-07 10:34:21 -05:00
RadWolfie
774ef7c9e3 fix pull request 2348 regression 2022-05-02 15:30:07 -05:00
RadWolfie
9973ec7b6f fix tls data initialization from cxbxr's emulation threads end 2022-05-02 09:46:14 -05:00
ergo720
5e42d181f2
Merge pull request #2348 from RadWolfie/emu-gui-purge
Purge emulation codebase from GUI project
2022-04-25 14:09:45 +02:00
RadWolfie
23488ad22b review remark 2022-04-25 04:49:22 -05:00
RadWolfie
8b0b016aec more cleanup 2022-04-14 00:14:41 -05:00
RadWolfie
cd09cf8dfd remove emu codebase from gui's side 2022-04-14 00:14:41 -05:00
ergo720
374ba5ec70
Merge pull request #2345 from RadWolfie/init-thread-fix
Fix order of thread initialization
2022-04-13 15:29:31 +02:00
RadWolfie
3a50d7e136 fix order of thread initialization 2022-04-13 07:54:19 -05:00
PatrickvL
696d49820c
Merge pull request #2342 from NZJenkins/unc-xbes
Better support for UNC paths
2022-04-12 12:58:16 +02:00
Anthony
2a3656bf24 Some comments on the "mount point" 2022-04-12 19:26:31 +12:00
Anthony
469352e72a Better support for UNC paths
Fix launching demos when the title was loaded from a UNC path
e.g. MechAssault with quantum redshift demo

We use GetFinalPathNameByHandle, which returns a "root local device" path beginning with '\\?\'

For UNC paths, this looks like '\\?\UNC\bla' - but this path was not handled correctly.

Before, '\\?\' was immediately stripped from the beginning of the path,
resulting in an invalid relative path e.g 'UNC\bla' - causing errors.

Now, we don't strip anything from the path, and accept it as-is.

In some related code, we also need to use the '\' character instead of '/'
since '/' is not the path separator on Windows and is not valid to add to a path beginning with '\\?\'.

Info about paths windows paths
https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
2022-04-12 19:11:42 +12:00
NZJenkins
72908248eb
Merge pull request #2341 from NZJenkins/jsrf-storage-spaces
Clear FILE_NO_INTERMEDIATE_BUFFERING in IoCreateFile
2022-04-07 19:32:28 +12:00
Anthony
e9212c35f7 Fix annoying assert
asserting when D3DDECLTYPE_FLOAT1 was in use
2022-04-06 22:21:12 +12:00
ergo720
4b5bef273d
Merge pull request #2340 from RadWolfie/impl-thread-stack-reader
Implement Thread Stack Readers
2022-04-06 11:14:52 +02:00
RadWolfie
a338a12d77 revert partial review remark 2022-04-06 03:42:35 -05:00
Anthony
88b686c27d Clear FILE_NO_INTERMEDIATE_BUFFERING in IoCreateFile
Fixes JSRF issue with loading titles from Windows Storage Spaces
2022-04-06 19:49:02 +12:00
RadWolfie
b84e80663f review remark 2 2022-04-05 17:57:05 -05:00
RadWolfie
351c9a7c86 review remark 2022-04-05 17:45:36 -05:00
RadWolfie
6d395bf2ee fix alignment from tabs to spaces 2022-04-03 19:08:14 -05:00
RadWolfie
57640cad49 Implement RtlGetCallersAddress 2022-04-03 19:08:13 -05:00
RadWolfie
1d030aa41f Implement RtlCaptureStackBackTrace 2022-04-03 19:08:13 -05:00
RadWolfie
c723c56e4a Implement RtlWalkFrameChain 2022-04-03 19:08:13 -05:00
ergo720
5f7b9417b0
Merge pull request #2339 from RadWolfie/improve-thread-setup
Improve Xbox Thread Setup
2022-04-04 00:54:23 +02:00
RadWolfie
877c87537e review remark 2022-04-03 17:30:08 -05:00
ergo720
9f87854040
Merge pull request #2130 from RadWolfie/update-readme
Variant Update to Readme
2022-04-01 01:37:47 +02:00
RadWolfie
38eeef7c0d add readme to informational label 2022-03-31 18:11:03 -05:00
RadWolfie
5d87ad46c6 update speical thanks section 2022-03-31 17:49:24 -05:00
RadWolfie
7b9634a3ec fix new line in markdown 2022-03-31 17:36:19 -05:00
RadWolfie
8dd54df4a8 Review remarks 2022-03-31 17:35:17 -05:00
RadWolfie
c5fe198bb2 direct users from readme to official site's download page 2022-03-31 17:27:47 -05:00
RadWolfie
a81ec74d06 fixup wine usage 2022-03-31 17:25:47 -05:00
RadWolfie
82427abcb8 fix hidden bug when binaries are mixed 2022-03-29 20:36:58 -05:00
RadWolfie
f0a1301b64 improve xbox thread implement 2022-03-29 20:36:58 -05:00
RadWolfie
d9785e7225 prepare and clean up kernel types 2022-03-29 20:36:58 -05:00
RadWolfie
067aa82cc7
Merge pull request #2337 from ergo720/mmglobaldata
Fixed wrong type for MmGlobalData kernel export
2022-03-29 08:23:22 -05:00
ergo720
5d5fc992da Fixed wrong type for MmGlobalData kernel export 2022-03-29 14:47:00 +02:00
ergo720
dc1f93b120
Merge pull request #2326 from NZJenkins/exp/dsound-buffer-streaming
DirectSoundBuffer streaming
2022-03-29 14:03:54 +02:00
RadWolfie
d0b4c6abac
Merge pull request #2336 from ergo720/sdl_update
Updated SDL submodule to 2.0.20 stable version
2022-03-28 20:15:00 -05:00
ergo720
09c5eb405d Updated SDL submodule to 2.0.20 2022-03-28 18:40:39 +02:00
RadWolfie
3908a8c7b2
Merge pull request #2334 from ergo720/C++20
Updated cxbxr projects to use and build with C++20
2022-03-28 04:18:36 -05:00
ergo720
51ddb6c1d1 Dropped VS2019 builds and updated readme 2022-03-28 10:53:46 +02:00
ergo720
bfa9abbc1b Updated cxbxr projects to use and build with C++20 2022-03-22 00:52:52 +01:00
Anthony
aac207c78e Fix typo 2022-03-14 17:10:26 +13:00
Anthony
49b4988953 Co-locate imgui includes 2022-03-13 21:41:13 +13:00
Anthony
da27e1456b Restore Unlock logging 2022-03-12 09:57:32 +13:00
Anthony
baa1cf5470 Ensure End() is called after Begin/BeginChild, regardless of return value
Note this is not required for other Begin apis
2022-03-12 09:53:22 +13:00
Anthony
feb1f0383d Comment some includes 2022-03-12 09:45:23 +13:00
RadWolfie
6788bf16f3
Merge pull request #2328 from Margen67/actions
CI: Upgrade checkout+upload/download-artifact to v3+labeler to v4
2022-03-11 02:25:13 -06:00
Margen67
b1446a5f6b Other workflow improvements
autoclose:
 Use checkout v3.
pull-request:
 Use labeler v4.
2022-03-10 23:56:00 -08:00
Margen67
0a72fbf7f7 CI: Upgrade checkout+upload/download-artifact to v3 2022-03-09 13:45:50 -08:00
Anthony
4186d4ba8b Avoid expensive calls to DirectSound on buffers unless they've been played at least once 2022-03-07 21:24:35 +13:00
Anthony
4c30264136 DirectSoundBuffer visualization debug view
- Visualize play progress, play region, loop region
- Buffer streaming controls
- Scale visualization to window by RadWolfie
2022-03-07 21:24:35 +13:00
Anthony
7528d9a4e3 Change locking APIs to nops
to prevent interference DirectSoundBuffer streaming.
2022-03-07 20:33:12 +13:00
Anthony
60dbf241e8 Stream audio from Xbox DirectSoundBuffers
to handle cases where titles write to sound buffers after they are created.
Note titles do not have to lock the buffer or otherwise call any API to write to sound buffers.

Every few ms, for each sound buffer currently playing, write a chunk of sound data ahead of the current audio play cursor.

Fixes audio issues in titles including:
- NBA Live 2005 (no audio)
- Crash Tag Team Racing (audio looping incorrectly)
- Madagascar (audio looping incorrectly)
2022-03-07 20:33:11 +13:00
Anthony
852adf0d21 optimize DirectSound Buffer's StopEx function 2022-03-06 21:00:02 +13:00
Luke Usher
1e300d63ec
Merge pull request #2310 from Margen67/actions
CI: Upgrade to VS2022
2022-03-04 09:02:15 +00:00
Margen67
5b0cf41507 Update README 2022-03-04 00:26:57 -08:00
Margen67
0c8dd778d0 CI: Upgrade to VS2022
Remove windows-2016 since it's going to be removed soon;
 actions/virtual-environments#4312
Use ${GITHUB_SHA::7} for tag to prevent commit weirdness.
2022-03-03 21:48:08 -08:00
RadWolfie
25b9a2efcc
Merge pull request #2325 from LukeUsher/log-failed-patches
hle: detect when patches fail to apply
2022-03-01 17:00:42 -06:00
Luke Usher
3a59c62753
Update src/core/hle/Patches.cpp
Co-authored-by: RadWolfie <RadWolfie@users.noreply.github.com>
2022-03-01 22:41:18 +00:00
Luke Usher
884a9080b5 hle: detect when patches fail to apply 2022-03-01 20:40:56 +00:00
ergo720
f857593f77
Merge pull request #2315 from ergo720/thread_ob_handle
Updated Ps kernel functions to use Ob handles + unpatch thread XAPI functions + added APC support to kernel via Ob
2022-02-22 23:20:29 +01:00
ergo720
d3b2554b20 Use a separate array for the native handles in NtWaitForMultipleObjectsEx 2022-02-20 16:07:42 +01:00
ergo720
f174872702 Unpatch timeSetEvent and timeKillEvent + fixes a crash in chihiro games + missing audio effects in virtua cop 3 + fixes a bug in SignalObjectAndWait 2022-02-20 12:22:36 +01:00
ergo720
c6ea72dcf4 Use a condition variable to notify interrupts, fixes stuttering in the dashboard 2022-02-18 20:17:22 +01:00
ergo720
bc98e164b2 Removed CxbxKrnlTerminateThread and some unnecessary calls to TerminateProcess 2022-02-18 15:30:18 +01:00
ergo720
6867907a3c Fixed a bug in NtWaitForMultipleObjectsEx that caused the dashboard to deadlock + more review remarks 2022-02-18 12:42:56 +01:00
ergo720
7589f0a94c Avoid using std::async in WaitApc 2022-02-17 02:33:44 +01:00
RadWolfie
a769e896c6 hidden bug while debugging on xbox kernel thread's issue 2022-02-16 18:20:04 +01:00
RadWolfie
44ed2ee3aa update Timer_Shutdown to reduce wait time for shutdown if threads are all xbox 2022-02-16 18:20:04 +01:00
ergo720
06f34134ff Review remarks + use PsCreateSystemThread to start all xbox threads 2022-02-16 18:20:04 +01:00
RadWolfie
6320dd5539 fix shutdown process crashed on shutdown and reboots 2022-02-16 18:20:03 +01:00
RadWolfie
8c7247abf5 reimplement suspend xbox threads so we can shutdown emulation properly 2022-02-16 18:20:02 +01:00
RadWolfie
0b90a48434 register duplicated xbox handle require duplicated handle from host 2022-02-16 18:20:01 +01:00
ergo720
e85af190d5 Properly set the ref count of ethread, fixes Amped 2022-02-16 18:20:01 +01:00
ergo720
733670c7f8 Fixed an issue with xbox handle registration 2022-02-16 18:20:01 +01:00
ergo720
79ac0c3019 Updated thread timings in ethread + null id upon thread termination 2022-02-16 18:20:00 +01:00
ergo720
ec6b16c68a Added support to Ps notification routines 2022-02-16 18:20:00 +01:00
ergo720
e9cc351bba Unpatch XSetProcessQuantumLength + moved unused xapi patched to standalone file + added code to handle xbox user APCs in SignalObjectAndWait 2022-02-16 18:19:59 +01:00
RadWolfie
e208c73586 fix thread calls issue from GetNativeHandle to keep special handle return 2022-02-16 18:19:59 +01:00
RadWolfie
7e5f9a7cb7 unpatch GetExitCodeThread 2022-02-16 18:19:58 +01:00
RadWolfie
b39801df11 unpatch SetThreadPriority, GetThreadPriority, SetThreadPriorityBoost 2022-02-16 18:19:58 +01:00
ergo720
a791b7609c Updated KeDelayExecutionThread, KeSetBasePriorityThread and XAPI thread functions to accept ob handles + more bug fixes 2022-02-16 18:19:58 +01:00
ergo720
b664488274 Bug fixes 2022-02-16 18:19:57 +01:00
ergo720
114be1b7c9 Added APCs support to kernel 2022-02-16 18:19:57 +01:00
ergo720
607a48e3ea Update Nt functions that accept thread handles (except for NtQueueApcThread) 2022-02-16 18:19:56 +01:00
ergo720
9082891903 Make Ps functions use Ob to create thread handles 2022-02-16 18:19:56 +01:00
Luke Usher
484a2c3f47
Merge pull request #2321 from PatrickvL/cleanup_system_selection
Cleanup system selection, so it's no longer abusing xbeType
2022-02-16 14:10:55 +00:00
PatrickvL
c883034372 Cleanup system selection, so it's no longer abusing xbeType
Rename g_bIsDebug into g_bIsDevKit for consistency with system selection
Cleaned up related comments
2022-02-16 14:56:45 +01:00
Luke Usher
41d45dd88d
Merge pull request #2320 from ergo720/fix_port_func_arg
Fix incorrect type in port io kernel functions
2022-02-06 10:57:12 +00:00
Luke Usher
86022747f0
Merge pull request #2319 from RadWolfie/init-exception-manager-early
Move Crash Manager's Init at Beginning of Emulation & Fix Hidden Crash
2022-02-06 10:56:44 +00:00
ergo720
1125c1c45d Fix incorrect type in port io kernel functions 2022-02-06 11:33:58 +01:00
RadWolfie
7d116628c2 make complete set of FS instructions for offset 0x00 and 0x04 to retreive and set 2022-02-05 17:33:09 -06:00
RadWolfie
f41cc02c6c disable overwrite host's stack data 2022-02-04 13:15:07 -06:00
RadWolfie
da72da4d03 initialize exception manager at beginning of emulation process 2022-02-04 12:29:05 -06:00
PatrickvL
db1bae2d4e
Merge pull request #2318 from GXTX/port_fixes
Fix kernel type hints for READ_PORT_BUFFER and friends
2022-02-04 08:59:19 +01:00
wutno
59fe8eb6db Fix kernel type hints for READ_PORT_BUFFER and friends 2022-02-04 02:20:27 -05:00
Luke Usher
be4fb1deb6
Merge pull request #2312 from RadWolfie/fix-uem-led-status
Fix UEM and LED Status
2022-01-28 16:37:49 +00:00
RadWolfie
1e05973b81
Merge pull request #2301 from Margen67/net
Upgrade CxbxDebugger to .NET Framework 4.8
2022-01-28 10:24:08 -06:00
RadWolfie
79b3b4e803
Merge pull request #2308 from Margen67/labeler
labeler.yml: Consistently use quotation marks
2022-01-23 02:41:19 -06:00
Margen67
02b9d75b38
Update .github/labeler.yml
Co-authored-by: RadWolfie <RadWolfie@users.noreply.github.com>
2022-01-23 00:37:47 -08:00
Margen67
349f28c6cb Upgrade CxbxDebugger to .NET Framework 4.8 2022-01-19 15:59:17 -06:00
RadWolfie
4625a34eec fix LED status and emulation state 2022-01-18 13:37:05 -06:00
RadWolfie
5ac2d3e152 fix UEM screen not showing up every time 2022-01-18 13:37:04 -06:00
Luke Usher
1b10e1b9d4
Merge pull request #2298 from RadWolfie/sync-xbsdb
Sync XbSymbolDatabase + Add Missing Patch
2022-01-16 15:35:07 +00:00
Margen67
822f4f9b9a
labeler.yml: Consistently use quotation marks 2022-01-14 12:21:55 -08:00
Luke Usher
c65d26a284
Merge pull request #2299 from Margen67/actions
CI.yml: Minor improvements
2022-01-14 14:07:27 +00:00
RadWolfie
54d3ee11bb add missing patch for D3DDevice_GetBackBuffer2_0__LTCG_eax1 2022-01-12 00:30:54 -06:00
RadWolfie
50b969549f sync XbSymbolDatabase 2022-01-12 00:30:54 -06:00
Luke Usher
f8b449d6b1
Merge pull request #2306 from NZJenkins/sc-lighting
Fixup sampling cube textures with PROJECT2D
2022-01-10 09:15:00 +00:00
Anthony
a844dffa95 Fixup sampling cube textures with PROJECT2D
Use CUBEMAP sampling instead
Fixes lighting in Splinter Cell Chaos Theory
2022-01-09 22:50:59 +13:00
Luke Usher
a25e455289
Merge pull request #2305 from CookiePLMonster/adl-cpusets
Fixup Win10 CPU Sets to check the EfficiencyClass
2021-12-30 21:42:14 +00:00
Silent
f41f73f6c3
Fixup Win10 CPU Sets to check the EfficiencyClass
Instead of picking the first physical core for Xbox threads,
Cxbx-R now picks the first highest performance physical core.
While at least Alder Lake lists performance cores first,
this is not guaranteed to always be the case in the future.
2021-12-30 21:36:20 +01:00
RadWolfie
283331412a
Merge pull request #2302 from ergo720/ob_fix
Allow ObIniSystem to succeed
2021-12-28 18:47:14 -06:00
ergo720
6078193fec Fixed a bug in ObOpenObjectByName 2021-12-27 16:04:57 +01:00
ergo720
922bd4e9d2 Added ObLock and ObUnlock 2021-12-27 16:04:57 +01:00
ergo720
a7cf67cd71 Allow ObIniSystem to succeed 2021-12-27 11:58:09 +01:00
Margen67
c1fb2d665b CI.yml: Minor improvements
Ignore more files.
Rename os to runs-on for consistency.
Add VS2022. (commented out due to #2300)
Remove working-directory.
Make release stat condition use single brackets;
 Same behavior, but two less characters.
2021-12-22 11:16:32 -08:00
Luke Usher
3e5272d91a
Merge pull request #2284 from NZJenkins/swap-copy
Tweak Swap behaviour to fix flickering in some titles
2021-12-22 11:03:15 +00:00
RadWolfie
e77404472e
Merge pull request #2297 from ergo720/jedi_fix
Simulate correctly the USB enumeration process in XGetDevices
2021-12-12 15:40:16 -06:00
ergo720
ef5c9ad4fb Simulate correctly the USB enumeration process in XGetDevices 2021-12-12 21:59:20 +01:00
ergo720
0c28aca19c
Merge pull request #2295 from RadWolfie/libusb-rollback
Rollback libusb to v1.0.24 release tag instead of wip development
2021-12-11 02:05:02 +01:00
ergo720
7c630d6104
Merge pull request #2296 from ergo720/xbox_constants
Switched some xbox ntstatus codes to uppercase
2021-12-09 13:08:41 +01:00
ergo720
29e1dc865d Switched to uppercase for xbox ntstatuses 2021-12-04 23:09:09 +01:00
RadWolfie
c9fe07ab98
Merge pull request #2294 from ergo720/lightgun
Added support for the lightgun input device
2021-11-29 12:33:23 -06:00
ergo720
0b2c0a2e33 review remarks 2021-11-29 14:45:24 +01:00
ergo720
66b83d98e4 Bug fixes 2021-11-27 23:53:54 +01:00
ergo720
caf7927445 Added lightgun laser emulation 2021-11-27 23:53:53 +01:00
ergo720
c658777645 Added lightgun support to input manager 2021-11-27 23:53:51 +01:00
ergo720
2c1f5bd430 Added lightgun support to xapi 2021-11-27 23:53:50 +01:00
ergo720
4645d42130 Added lightgun support to input gui 2021-11-27 23:53:49 +01:00
RadWolfie
41c9d7a352
Merge pull request #2292 from Fisherman166/SecureTrayEject
Implement HalEnableSecureTrayEject xbox kernel function.
2021-11-25 13:03:29 -06:00
Dartht33bagger
84ea340da1 Add TODO for implementing SMC_COMMAND_RESET_ON_EJECT in the future. 2021-11-25 09:56:08 -08:00
RadWolfie
6d08f2a99b rollback libusb to v1.0.24 release tag instead of wip development 2021-11-21 21:47:12 -06:00
Fisherman166
886bef5c8a Implement HalEnableSecureTrayEject xbox kernel function. 2021-11-13 17:15:27 -08:00
ergo720
4457d110bf
Merge pull request #2291 from RadWolfie/update-xbsymboldatabase
Update XbSymbolDatabase Module
2021-11-09 18:17:09 +01:00
RadWolfie
8e424759df update XbSymbolDatabase module 2021-11-08 11:44:51 -06:00
ergo720
f7042be933
Merge pull request #2290 from ergo720/hw_passthrough
Add support for original xbox gamepads and SBC hardware via USB passthrough with libusb
2021-11-08 11:46:59 +01:00
ergo720
7791c9dd78 Review remarks 2021-11-06 19:19:38 +01:00
ergo720
c76de904dc Added WinUSB requirement to README 2021-11-06 14:29:37 +01:00
ergo720
5a01ce5297 Fixed wrong SBC subtype + fixed off by two bytes rumble struct + removed rumble hack + switched to interrupt transfers 2021-11-06 14:29:37 +01:00
ergo720
1886819a35 Added a hack(?) to get rumble working with libusb 2021-11-06 14:29:37 +01:00
ergo720
6a9e242fad Added gui for libusb devices + bug fixes 2021-11-06 14:29:37 +01:00
ergo720
9092f7bbe6 Extended LibusbDevice class + allowed xapi to work with libusb devices 2021-11-01 17:03:40 +01:00
ergo720
006af26a6e Added libusb class + libusb log option 2021-11-01 17:03:40 +01:00
ergo720
b748c5f61a Added libusb submodule
Co-authored-by: Fred Hallock <specialfred453@gmail.com>
2021-10-31 12:39:25 +01:00
Luke Usher
94f02583ba
Merge pull request #2286 from CookiePLMonster/simplify-timers
Simplify timers
2021-10-16 16:23:57 +01:00
Luke Usher
ac5289d83a
Merge pull request #2288 from ergo720/clang
Fixed some code errors detected by clang
2021-10-16 16:23:21 +01:00
ergo720
51c3f37596 Fixed non-const lvalue reference to type 'std::from_chars_result' cannot bind to a temporary of type 'std::from_chars_result' 2021-10-12 19:02:42 +02:00
ergo720
ca5a1aaa71 Fixed non-const lvalue reference to type cannot bind to a temporary of type 2021-10-12 18:44:09 +02:00
ergo720
23c72d825e Fixed cannot pass object of non-trivial type 'std::vector<char>' through variadic function; call will abort at runtime [-Wnon-pod-varargs] 2021-10-12 17:35:02 +02:00
ergo720
4f26ab927f Fixed explicit instantiation of undefined function template 'BindDefault' 2021-10-12 17:22:52 +02:00
ergo720
309975da61 Fixed subscript of pointer to incomplete type 'struct _XBE_SECTION' 2021-10-12 17:06:31 +02:00
ergo720
be56eac811 Fixed constexpr function never produces a constant expression [-Winvalid-constexpr] 2021-10-12 17:02:07 +02:00
Silent
326a5bb714
Simplify timer code to scale them without state 2021-10-09 18:15:21 +02:00
ergo720
3bdd689e03
Merge pull request #2285 from ergo720/xiso_workaround
Added workaround for dokany bug when reading files inside xisos mounted by xbox-iso-vfs
2021-10-07 23:08:54 +02:00
ergo720
9b65924898 Added workaround for dokany bug when reading files inside xisos mounted by xbox-iso-vfs 2021-10-07 22:50:16 +02:00
Anthony
b863432904 Add LOG_TEST_CASE for uncommon swap flags 2021-10-07 21:38:08 +13:00
Anthony
32fa33ddab Fix MotoGP flicker
Hack to handle BYPASSCOPY
2021-10-07 00:30:20 +13:00
PatrickvL
629d6d2054
Merge pull request #2282 from ergo720/freopen_invalid_handle
Fixed invalid handle exception in freopen
2021-10-03 12:14:22 +02:00
ergo720
b52f5655e4 Added comment explaining the DETACHED_PROCESS flag 2021-10-03 12:08:47 +02:00
ergo720
35072da2ea Fixed invalid handle exception in freopen 2021-10-03 10:15:48 +02:00
Anthony
b804ed1f03 Fix flicker in antialias sample
Improve X_D3DSWAP_COPY behaviour
2021-09-14 20:43:17 +12:00
Luke Usher
ac68fd481c
Merge pull request #2280 from ergo720/log_fix
Fixed insufficient size of LoggedModules variable
2021-09-06 16:48:47 +01:00
ergo720
9b02cac7ad Fixed insufficient size of LoggedModules variable 2021-09-06 17:04:37 +02:00
ergo720
b405153c7e
Merge pull request #2271 from RadWolfie/cleanup-kernel-process
Some cleanup kernel process
2021-08-31 00:32:30 +02:00
RadWolfie
bc6c017b7a kernel: replace __declspec(noreturn) to C++'s [[noreturn]] usage 2021-08-30 12:37:22 -05:00
RadWolfie
9bf21223f2 kernel: rename CxbxKrnlCleanup(Ex) to CxbxrKrnlAbort(Ex) 2021-08-30 12:27:03 -05:00
RadWolfie
2c46ea3d98 kernel: make CxbxrKrnlInitHacks function 2021-08-30 12:27:03 -05:00
RadWolfie
cc6041d2de kernel: make CxbxrLogDumpXbeInfo function 2021-08-30 12:27:03 -05:00
RadWolfie
5e26b938fb add todo comment for ApplyMediaPatches function 2021-08-30 12:27:03 -05:00
RadWolfie
f3dc44ebae kernel: move ApplyMediaPatches function 2021-08-30 12:27:03 -05:00
RadWolfie
5692f79d78 kernel: make CxbxrKrnlSetupDummyHeader function 2021-08-30 12:26:57 -05:00
RadWolfie
093c23d5bc kernel: make CxbxrKrnlPrepareXbeMap function 2021-08-30 12:26:57 -05:00
RadWolfie
249d6b2169 kernel: break down CdRom0 relative code into functions 2021-08-30 12:26:57 -05:00
RadWolfie
0e5e791a55 kernel: make CxbxrKrnlRegisterDevicePaths function 2021-08-30 12:26:57 -05:00
RadWolfie
16ea4519c8 kernel: breakdown xbe detector for system type to use 2021-08-30 12:26:57 -05:00
RadWolfie
93b5e88754 kernel: make CxbxrKrnlSyncGUI function 2021-08-30 12:26:57 -05:00
RadWolfie
08ee4a15d3 kernel: move relative console and file output into CxbxrKrnlSetupVerboseLog function 2021-08-30 12:26:57 -05:00
RadWolfie
cefea8ad83 kernel: rename szFolder_CxbxReloadedData to g_DataFilePath and convert to std::string 2021-08-30 12:26:56 -05:00
RadWolfie
094256ef43 kernel: more clean up for file path setup 2021-08-30 12:26:47 -05:00
RadWolfie
8de8f411c1 kernel: move rdtsc patches relative into its own source file. 2021-08-30 12:23:50 -05:00
RadWolfie
0c2b7b4220 kernel: move relative file paths into its own header file. 2021-08-30 12:23:47 -05:00
PatrickvL
a5fa40956a
Merge pull request #2278 from Shideravan/patch-1
Changing the link for the license page
2021-08-22 09:00:25 +02:00
Shideravan
edffb3a128
Changing the link for the license page 2021-08-21 23:40:07 -03:00
RadWolfie
0fb2e6208d kernel: rename (G|S)etStorageLocation to (G|S)etDataLocation 2021-07-31 18:03:40 -05:00
RadWolfie
e1a8391170 imgui: move fps updater functions into imgui ui class 2021-07-31 18:03:40 -05:00
RadWolfie
b11cb57b0b
Merge pull request #2269 from Cxbx-Reloaded/LukeUsher-patch-1
Remove game-compatibility github link from template
2021-07-29 01:00:50 -05:00
Luke Usher
50d50288a5
Update .github/ISSUE_TEMPLATE/issue-template.md
Co-authored-by: RadWolfie <RadWolfie@users.noreply.github.com>
2021-07-28 11:49:06 +01:00
Luke Usher
9439b9f54c Remove game-compatibility github link from template 2021-07-27 09:32:35 +01:00
RadWolfie
de16fe345a
Merge pull request #2268 from ergo720/create_process_error_str
Print the windows error string when CreateProcess fails to create the new emulation process
2021-07-26 12:14:01 -05:00
ergo720
5e7c6a082f Print the windows error string when CreateProcess fails to create the emulation process 2021-07-26 18:36:56 +02:00
Luke Usher
a6e17bc4de
Merge pull request #2267 from NZJenkins/leave-path
Fix loading from UNC paths
2021-07-26 12:05:24 +01:00
Anthony
660e6bca1e Fix loading from UNC paths
Remove unexplained slash processing
which breaks UNC paths
2021-07-26 22:36:28 +12:00
Luke Usher
b4dadb1dff
Merge pull request #2266 from ergo720/xbe_launch_error_reword
Reworded error message displayed when cxbxr fails to launch an xbe
2021-07-21 12:51:54 +01:00
ergo720
e37bb218ba Reworded error message displayed when cxbxr fails to launch an xbe 2021-07-21 13:19:28 +02:00
PatrickvL
30956c1044
Merge pull request #2261 from ergo720/emulog_cdecl
Removed stdcall from EmuLog functions
2021-07-14 15:55:50 +02:00
ergo720
cb3330a9e4 Removed stdcall from EmuLog 2021-07-14 15:38:45 +02:00
ergo720
d42e3ee271
Merge pull request #2259 from ergo720/title_short
Removed unnecessary info from the cxbxr title string
2021-07-14 12:31:40 +02:00
ergo720
cf8ff008a8 Shorten cxbxr title string 2021-07-14 10:55:45 +02:00
Luke Usher
74c8b0cde1
Merge pull request #2257 from RadWolfie/dsound-getstatus-hack-workaround
HACK: DS Stream GetStatus function workaround mimic hack.
2021-07-08 09:26:06 +01:00
RadWolfie
390466e615 dsound: GetStatus workaround mimic hack. 2021-07-05 11:10:42 -05:00
ergo720
6e68433dbd
Merge pull request #2254 from RadWolfie/cleanup-emufile
Small Clean Up Portion of EmuFile Usage
2021-07-05 11:25:01 +02:00
RadWolfie
edad809de0 clean up portion of EmuFile usage 2021-06-29 09:15:50 -05:00
209 changed files with 12746 additions and 6497 deletions

View file

@ -4,14 +4,16 @@ about: Issue template.
title: ''
labels: ''
assignees: ''
---
<!--
ISSUES NOT UTILIZING THE TEMPLATE BELOW WILL BE CLOSED!
-->
<!--
Please read https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/blob/master/README.md and https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/wiki/Frequently-Asked-Questions-(FAQ) before opening an issue.
Remember, the GitHub Issue Tracker is not the place to ask for support or to submit game compatibility https://github.com/Cxbx-Reloaded/game-compatibility/blob/master/README.md reports.
You must use our forum on Discord https://discord.gg/26Xjx23 for that.
Remember, the GitHub Issue Tracker is not the place to ask for support. You must use our forum on Discord https://discord.gg/26Xjx23 for that.
Compatibility Reports should be submitted at the website: https://cxbx-reloaded.co.uk
Otherwise, for any other emulation/general issues like crashes when a controller is connected, or regressions across several titles, feel free to report your issue here.
-->

103
.github/labeler.yml vendored
View file

@ -1,74 +1,111 @@
#labels are sorted by alphabet order.
# Labels are in alphabetical order.
cmake:
- CMake*
- changed-files:
- any-glob-to-any-file:
- 'CMake*'
- '**/CMakeLists.txt'
- '**/*.cmake'
cpu-emulation:
- src/devices/x86/**
- changed-files:
- any-glob-to-any-file:
- 'src/devices/x86/**'
deployment:
- changed-files:
- any-glob-to-any-file:
- '*.yml'
- .github/workflows/CI.yml
- '.github/workflows/CI.yml'
file-system:
- src/core/kernel/support/EmuFile*
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/support/EmuFile*'
graphics:
- src/core/hle/D3D8/**
- src/core/hle/XGRAPHIC/**
- src/devices/video/**
- src/gui/*Video*
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/D3D8/**'
- 'src/core/hle/XGRAPHIC/**'
- 'src/devices/video/**'
- 'src/gui/*Video*'
HLE:
- src/core/hle/**
- src/core/kernel/**
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/**'
- 'src/core/kernel/**'
informational:
- changed-files:
- any-glob-to-any-file:
- '**/*Logging*'
- '**/*Logging*/**'
- '**/README.md'
input:
- src/common/input/**
- src/core/hle/XAPI/input/**
- src/devices/usb/**
- src/gui/controllers/**
- src/gui/*Input*
- changed-files:
- any-glob-to-any-file:
- 'src/common/input/**'
- 'src/core/hle/XAPI/input/**'
- 'src/devices/usb/**'
- 'src/gui/controllers/**'
- 'src/gui/*Input*'
kernel:
- src/core/kernel/**
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/**'
LLE:
- src/devices/**
- changed-files:
- any-glob-to-any-file:
- 'src/devices/**'
memory:
- src/core/kernel/memory-manager/**
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/memory-manager/**'
networking:
- src/core/hle/XONLINE/**
- src/devices/network/**
- src/gui/*Network*
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/XONLINE/**'
- 'src/devices/network/**'
- 'src/gui/*Network*'
sound:
- src/common/audio/**
- src/core/hle/DSOUND/**
- src/core/hle/XACTENG/**
- src/devices/audio/**
- src/gui/*Audio*
- changed-files:
- any-glob-to-any-file:
- 'src/common/audio/**'
- 'src/core/hle/DSOUND/**'
- 'src/core/hle/XACTENG/**'
- 'src/devices/audio/**'
- 'src/gui/*Audio*'
modules:
- import/**
- changed-files:
- any-glob-to-any-file:
- 'import/**'
threading:
- src/core/kernel/support/EmuFS*
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/support/EmuFS*'
timing:
- src/common/Timer*
- changed-files:
- any-glob-to-any-file:
- 'src/common/Timer*'
user interface:
- src/gui/**
# - TODO: Dear Imgui location, maybe in src/core/gui path.
- changed-files:
- any-glob-to-any-file:
- 'src/core/common/imgui/*'
- 'src/gui/**'
xbdm:
- src/common/xbdm/**
- changed-files:
- any-glob-to-any-file:
- 'src/common/xbdm/**'

View file

@ -3,51 +3,51 @@ name: GitHub CI
on:
push:
paths-ignore:
- '.gitattributes'
- '.github/*'
- '.github/*_TEMPLATE/**'
- '.gitignore'
- '*.bat'
- '*.yml'
- 'doc/**'
pull_request:
paths-ignore:
- '.gitattributes'
- '.github/*'
- '.github/*_TEMPLATE/**'
- '.gitignore'
- '*.bat'
- '*.yml'
- 'doc/**'
jobs:
build-windows:
runs-on: ${{ matrix.os }}
name: Build (Windows, ${{ matrix.configuration }}, VS${{ matrix.vsver }}) # runner.os doesn't work here
runs-on: windows-${{ matrix.vsver }}
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
strategy:
fail-fast: false
matrix:
configuration: [Release, Debug]
vsver: [VS2019, VS2017]
include:
- vsver: VS2019
os: windows-latest
- vsver: VS2017
os: windows-2016 # https://github.com/actions/virtual-environments/issues/68#issuecomment-602652021
vsver: [2019]
winver: [7]
sdkver: [10.0.22621.0]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Generate CMake files
run: cmake -B build -A Win32
run: cmake -B build -A Win32,version=${{ matrix.sdkver }} -DCMAKE_SYSTEM_VERSION=${{ matrix.winver }}
- name: Build
working-directory: build
run: cmake --build . --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS
run: cmake --build build --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS
- name: Prepare artifacts
if: matrix.configuration == 'Release'
working-directory: build
run: cmake --install . --config ${{ matrix.configuration }} --prefix ../artifacts
- uses: actions/upload-artifact@v2
run: cmake --install build --config ${{ matrix.configuration }} --prefix artifacts
- uses: actions/upload-artifact@v4
if: matrix.configuration == 'Release'
with:
name: CxbxReloaded-${{ matrix.configuration }}-${{ matrix.vsver }}
name: CxbxReloaded-${{ matrix.configuration }}-VS${{ matrix.vsver }}
path: artifacts/bin
if-no-files-found: error
@ -59,23 +59,23 @@ jobs:
needs: build-windows
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Download Artifacts
uses: actions/download-artifact@v2
- uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Re-zip Artifacts
id: git
- name: Re-zip artifacts
id: zip
run: |
for artifact in artifacts/*; do
7z a $artifact.zip "./$artifact/*"
if [[ $(stat -c %s $artifact.zip) -le 1000 ]]; then
echo "Error: Archive $artifact.zip too small!"
7z a ${artifact}.zip "./${artifact}/*"
if [ $(stat -c %s ${artifact}.zip) -le 100000 ]; then
echo "Error: Archive ${artifact}.zip too small!"
exit 1
fi
done
echo "::set-output name=tag_name::CI-$(git rev-parse --short HEAD)"
echo "tag_name=CI-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release create ${{ steps.git.outputs.tag_name }} artifacts/*.zip -p --target $GITHUB_SHA --title '${{ steps.git.outputs.tag_name }}'
run: gh release create ${{ steps.zip.outputs.tag_name }} artifacts/*.zip -p --target $GITHUB_SHA --title '${{ steps.zip.outputs.tag_name }}'

View file

@ -1,15 +1,15 @@
on:
issues:
types: [opened]
types: opened
jobs:
auto_close_issues:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Automatically close issues that don't follow the issue template
uses: ergo720/auto-close-issues@v1.0.4
uses: ergo720/auto-close-issues@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
issue-close-message: "@${issue.user.login}: your issue has been automatically closed because it does not follow the issue template." # optional property

View file

@ -1,14 +1,13 @@
name: Pull Request Manager
on:
pull_request_target:
on: pull_request_target
jobs:
pr_manager:
runs-on: ubuntu-latest
steps:
- name: Labeler
uses: actions/labeler@v3
uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
sync-labels: true

14
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "import/subhook"]
path = import/subhook
url = https://github.com/Zeex/subhook.git
url = https://github.com/Cxbx-Reloaded/subhook.git
shallow = true
[submodule "import/cs_x86"]
path = import/cs_x86
@ -35,3 +35,15 @@
path = import/SDL2
url = https://github.com/libsdl-org/SDL
shallow = true
[submodule "import/libusb"]
path = import/libusb
url = https://github.com/Cxbx-Reloaded/libusb
branch = deadlock_fix
shallow = true
[submodule "import/nv2a_vsh_cpu"]
path = import/nv2a_vsh_cpu
url = https://github.com/abaire/nv2a_vsh_cpu.git
[submodule "import/mio"]
path = import/mio
url = https://github.com/mandreyel/mio.git
shadow = true

View file

@ -22,9 +22,7 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/XbSymbolDatabase")
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/SDL2" EXCLUDE_FROM_ALL)
if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])")
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/cs_x86")
endif()
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/mio" EXCLUDE_FROM_ALL)
# Cxbx-Reloaded projects
set(CXBXR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR})
@ -41,6 +39,11 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libtom")
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/imgui")
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libusb")
set(nv2a_vsh_cpu_UNIT_TEST OFF)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/nv2a_vsh_cpu" EXCLUDE_FROM_ALL)
# Split the files into group for which project is likely
# going to be used for both header and source files.
# Then move only specific project files into their
@ -51,16 +54,20 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/imgui")
# Common (GUI and Emulator)
file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.h"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuDes.h"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuRsa.h"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuSha.h"
"${CXBXR_ROOT_DIR}/src/common/crypto/LibRc4.h"
"${CXBXR_ROOT_DIR}/src/common/CxbxDebugger.h"
"${CXBXR_ROOT_DIR}/src/common/cxbxr.hpp"
"${CXBXR_ROOT_DIR}/src/common/EmuEEPROM.h"
"${CXBXR_ROOT_DIR}/src/common/Error.h"
"${CXBXR_ROOT_DIR}/src/common/FilePaths.hpp"
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardCodes.h"
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.h"
"${CXBXR_ROOT_DIR}/src/common/input/layout_xbox_device.h"
"${CXBXR_ROOT_DIR}/src/common/input/LibusbDevice.h"
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.h"
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.h"
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.h"
@ -68,13 +75,10 @@ file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.h"
"${CXBXR_ROOT_DIR}/src/common/IPCHybrid.hpp"
"${CXBXR_ROOT_DIR}/src/common/Logging.h"
"${CXBXR_ROOT_DIR}/src/common/ReservedMemory.h"
"${CXBXR_ROOT_DIR}/src/common/Settings.hpp"
"${CXBXR_ROOT_DIR}/src/common/Timer.h"
"${CXBXR_ROOT_DIR}/src/common/util/cliConfig.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConverter.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/CPUID.h"
"${CXBXR_ROOT_DIR}/src/common/util/crc32c.h"
"${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.h"
"${CXBXR_ROOT_DIR}/src/common/util/std_extend.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/strConverter.hpp"
@ -82,14 +86,20 @@ file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/win32/AlignPrefix1.h"
"${CXBXR_ROOT_DIR}/src/common/win32/EmuShared.h"
"${CXBXR_ROOT_DIR}/src/common/win32/Mutex.h"
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.h"
"${CXBXR_ROOT_DIR}/src/common/win32/Util.h"
"${CXBXR_ROOT_DIR}/src/common/win32/WineEnv.h"
"${CXBXR_ROOT_DIR}/src/common/xbdm/CxbxXbdm.h"
"${CXBXR_ROOT_DIR}/src/common/xbe/Xbe.h"
"${CXBXR_ROOT_DIR}/src/common/xbe/XbePrinter.h"
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.hpp"
"${CXBXR_ROOT_DIR}/src/common/xbox/Types.hpp"
"${CXBXR_ROOT_DIR}/src/common/xbox_types.h"
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/buffered_io.h"
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/xdvdfs.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Types.h"
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.h"
"${CXBXR_ROOT_DIR}/src/Cxbx.h"
"${CXBXR_ROOT_DIR}/src/CxbxVersion.h"
"${CXBXR_ROOT_DIR}/src/version.h"
@ -100,8 +110,10 @@ file (GLOB CXBXR_HEADER_GUIv1
"${CXBXR_ROOT_DIR}/src/common/input/Button.h"
"${CXBXR_ROOT_DIR}/src/common/input/EmuDevice.h"
"${CXBXR_ROOT_DIR}/src/common/input/InputWindow.h"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgDukeControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgSBControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgDukeControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLibusbControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLightgunConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgSBControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.h"
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/DlgInputConfig.h"
@ -120,12 +132,12 @@ file (GLOB CXBXR_HEADER_EMU_IMPORT
"${CXBXR_ROOT_DIR}/import/imgui/backends/imgui_impl_win32.h"
)
file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.h"
"${CXBXR_ROOT_DIR}/src/common/audio/converter.hpp"
"${CXBXR_ROOT_DIR}/src/common/audio/XADPCM.h"
"${CXBXR_ROOT_DIR}/src/common/Timer.h"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/glextensions.h"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen.h"
"${CXBXR_ROOT_DIR}/src/common/audio/XADPCM.h"
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.hpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.h"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/audio.hpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/ui.hpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/settings.h"
@ -133,6 +145,7 @@ file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/core/common/video/RenderBase.hpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxPixelShaderTemplate.hlsl"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxVertexShaderTemplate.hlsl"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Direct3D9.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
@ -141,13 +154,11 @@ file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/PixelShader.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Shader.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderCache.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Types.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPixelShader.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPushBuffer.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbState.h"
@ -166,15 +177,15 @@ file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/Intercept.hpp"
"${CXBXR_ROOT_DIR}/src/core/hle/Patches.hpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.h"
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.h"
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.h"
"${CXBXR_ROOT_DIR}/src/core/hle/XONLINE/XOnline.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/common/strings.hpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlAvModes.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKe.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKi.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPs.hpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/init/CxbxKrnl.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/init/KrnlPatches.hpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PhysicalMemory.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PoolManager.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/VMManager.h"
@ -182,6 +193,8 @@ file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuFile.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuFS.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuNtDll.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/NativeHandle.h"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/PatchRdtsc.hpp"
"${CXBXR_ROOT_DIR}/src/devices/ADM1032Device.h"
"${CXBXR_ROOT_DIR}/src/devices/EEPROMDevice.h"
"${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.h"
@ -220,39 +233,47 @@ list(FILTER CXBXR_HEADER_HLSL INCLUDE REGEX ".*\\.hlsl$")
# Common (GUI and Emulator)
file (GLOB CXBXR_SOURCE_COMMON
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.cpp"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuDes.cpp"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuRsa.cpp"
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuSha.cpp"
"${CXBXR_ROOT_DIR}/src/common/crypto/LibRc4.cpp"
"${CXBXR_ROOT_DIR}/src/common/CxbxDebugger.cpp"
"${CXBXR_ROOT_DIR}/src/common/cxbxr.cpp"
"${CXBXR_ROOT_DIR}/src/common/EmuEEPROM.cpp"
"${CXBXR_ROOT_DIR}/src/common/Error.cpp"
"${CXBXR_ROOT_DIR}/src/common/FilePaths.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/LibusbDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/XInputPad.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/Logging.cpp"
"${CXBXR_ROOT_DIR}/src/common/Settings.cpp"
"${CXBXR_ROOT_DIR}/src/common/Timer.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConfig.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConverter.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/crc32c.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/hasher.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/EmuShared.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/InlineFunc.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/IPCWindows.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Mutex.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Util.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/WineEnv.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbdm/CxbxXbdm.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbe/Xbe.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbe/XbePrinter.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbox/Types.cpp"
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/buffered_io.cpp"
"${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/xdvdfs.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXbox.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXc.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXe.cpp"
"${CXBXR_ROOT_DIR}/src/CxbxVersion.cpp"
"${CXBXR_ROOT_DIR}/src/HighPerformanceGraphicsEnabler.c"
)
@ -262,8 +283,10 @@ file (GLOB CXBXR_SOURCE_GUIv1
"${CXBXR_ROOT_DIR}/src/common/input/Button.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/EmuDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/InputWindow.cpp"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgDukeControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgSBControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgDukeControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLibusbControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLightgunConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgSBControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.cpp"
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/DlgInputConfig.cpp"
@ -288,12 +311,12 @@ file (GLOB CXBXR_SOURCE_EMU_IMPORT
)
file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_KRNL_CPP}"
"${CXBXR_ROOT_DIR}/src/common/AddressRanges.cpp"
"${CXBXR_ROOT_DIR}/src/common/VerifyAddressRanges.cpp"
"${CXBXR_ROOT_DIR}/src/common/Timer.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/glextensions.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen_common.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen_wgl.cpp"
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.cpp"
"${CXBXR_ROOT_DIR}/src/common/VerifyAddressRanges.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/Threads.cpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/audio.cpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/ui.cpp"
"${CXBXR_ROOT_DIR}/src/core/common/imgui/video.cpp"
@ -304,11 +327,10 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Shader.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/TextureStates.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderCache.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPixelShader.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbPushBuffer.cpp"
@ -326,9 +348,9 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalDSVoice.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalStruct.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/Intercept.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/JVS/JVS.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/Patches.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XAPI/Xapi.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnl.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlAv.cpp"
@ -340,16 +362,12 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKd.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKe.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlKi.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlLogging.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlMm.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlNt.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlOb.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPhy.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlPs.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlRtl.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXbox.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXc.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/EmuKrnlXe.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/exports/KernelThunk.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PhysicalMemory.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/memory-manager/PoolManager.cpp"
@ -358,7 +376,11 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuFile.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuFS.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/EmuNtDll.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/NativeHandle.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/PatchRdtsc.cpp"
"${CXBXR_ROOT_DIR}/src/devices/ADM1032Device.cpp"
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/JvsIO.cpp"
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/MediaBoard.cpp"
"${CXBXR_ROOT_DIR}/src/devices/EEPROMDevice.cpp"
"${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.cpp"
"${CXBXR_ROOT_DIR}/src/devices/MCPXDevice.cpp"
@ -417,10 +439,14 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/cxbxr-emu")
set(cxbxr_INSTALL_files "COPYING" "README.md")
# Cxbx-Reloaded project with third-party libraries
set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymbolDatabase libtommath libtomcrypt imgui
set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymbolDatabase libtommath libtomcrypt imgui libusb
PROPERTIES FOLDER Cxbx-Reloaded
)
set_target_properties(nv2a_vsh_emulator nv2a_vsh_disassembler nv2a_vsh_cpu
PROPERTIES FOLDER Cxbx-Reloaded/nv2a_vsh
)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Configure startup project
set_property(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" PROPERTY VS_STARTUP_PROJECT cxbx)
@ -448,14 +474,19 @@ install(FILES ${cxbxr_INSTALL_files}
DESTINATION bin
)
install(FILES
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/CxbxPixelShaderTemplate.hlsl"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
DESTINATION bin/hlsl
# Copy HLSL files to the output directory, which are loaded at runtime
set(CXBXR_HLSL_FILES ${CXBXR_HEADER_EMU})
list(FILTER CXBXR_HLSL_FILES INCLUDE REGEX ".*/src/core/hle/D3D8/Direct3D9/[^/]+\.hlsli?")
add_custom_command(
TARGET misc-batch POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CXBXR_HLSL_FILES} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl"
# These files can be edited.
# Create backup copies for convenience of restoring original shader behaviour.
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl/backup
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CXBXR_HLSL_FILES} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl/backup"
)
install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl DESTINATION bin)
set(cxbxr_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")

View file

@ -1,5 +1,5 @@
# Cxbx-Reloaded - Original Xbox Emulator
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](https://img.shields.io/badge/License-GPL%20v2-blue.svg)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/blob/master/COPYING)
[![GitHub Actions](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/workflows/GitHub%20CI/badge.svg?event=push)](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/actions?query=event%3Apush+workflow%3A%22GitHub+CI%22)
[![Discord](https://img.shields.io/badge/chat-on%20discord-7289da.svg?logo=discord)](https://discord.gg/26Xjx23)
@ -8,29 +8,32 @@ Cxbx-Reloaded is an emulator for running Microsoft Xbox (and eventually, Chihiro
## System Requirements
### Minimum
* OS: Windows 7+ x64, or x86-64 Linux with Wine. 32-bit is not supported.
* MacOS with Wine is known not to work, and BSD-based systems are untested.
* Also note that Wine is relatively unstable, and it might break compatibility with Cxbx-Reloaded at any time without warning.
* GPU: Direct3D 9.0c with Pixel Shader Model 2.x, and Vertex Shader Model 3.0.
## Prerequisites
### Windows
* [32-bit (x86) Visual C++ 2019 Redistributable](https://aka.ms/vs/16/release/vc_redist.x86.exe)
* [32-bit (x86) Visual C++ 2022 Redistributable](https://aka.ms/vs/17/release/vc_redist.x86.exe)
* [Npcap *(used for network emulation)*](https://nmap.org/npcap/#download)
* Make sure to enable winpcap compatibility mode!
* Make sure to enable winpcap compatibility mode.
* [WinUSB compliant driver](https://github.com/libusb/libusb/wiki/Windows#Driver_Installation)
* *Optional, only needed for USB pass-through of original Xbox and Steel Battalion controllers.*
### Wine
**NOTICE: Please use the latest stable release version of Wine. If it does not work for you, then roll back to Wine 5.0.3 which is the last known working version.**
**Please use the latest stable release version of Wine. If it does not work for you, then roll back to Wine 7.0 which is the last known working version.**<br/>
**There also exists this known [issue](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/2314) which currently prevents savings in some games with the most recent Wine 6.8 and later versions.**
* Winetricks
* `vcrun2017` or `vcrun2019`
* NOTE: vcrun2019 requires the latest winetricks script!
* `vcrun2019`
* Requires the latest winetricks script.
* `d3dcompiler_47`
* NOTE: May be subject to change over time.
* This may be subject to change.
* Winpcap is built-in, no installation is required.
## Automated Builds
Cxbx-Reloaded doesn't currently have stable builds, but you can obtain pre-release builds from the Releases tab, or the links below:
Cxbx-Reloaded doesn't currently have stable builds, but you can obtain pre-release builds from our official website's download page, or the links below:
* **[Release Builds](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/releases)**
* **Wine users will need to use `CxbxReloaded-Release-VS2017.zip` for it to run correctly.**
* **[Release Builds](https://cxbx-reloaded.co.uk/download)**
* *[Full build history](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/actions?query=workflow%3A%22GitHub+CI%22)*
## Compatibility
@ -42,15 +45,13 @@ If you would like to submit compatibility reports, please request permission in
Game or software specific issues can be reported in the [compatibility website](https://cxbx-reloaded.co.uk/compatibility).
For emulation issues that are not specific to any single piece of software, a bug report can be submitted at [the Cxbx-Reloaded issue tracker](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues).
Make sure to follow the issue template and that it contains:
<!--Make sure to follow the issue template and that it contains:
* The build tested with, error message displayed (if any)
* **(You can copy and paste any popup messages. However, please keep it clean by paste and trimming down to only the message itself.)**
* **You can copy and paste any popup messages. However, please keep it clean by pasting and trimming down to only the message itself.**
* Screenshots
* (optional unless has any graphic bug for references)
**NOTICE: Failure to follow template will auto close your ticket.**
* Optional unless there are graphic bugs for reference.
**Failure to follow the template will auto close your ticket.**-->
## Additional information
Cxbx-Reloaded has a [wiki](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/wiki) containing various subjects and background information.
@ -73,8 +74,7 @@ Please contact us before you start working on something, so we can make sure you
### Fetching the code
1. Run the following command in the command line:
`git clone --recurse-submodules https://github.com/Cxbx-Reloaded/Cxbx-Reloaded.git`
<br>`git clone --recurse-submodules https://github.com/Cxbx-Reloaded/Cxbx-Reloaded.git`
* Please note the `--recurse-submodules` parameter. This is required to fetch submodules.
* If Cxbx-Reloaded was checked out without submodules, they can be updated/fetched with the following command:
@ -83,10 +83,10 @@ Please contact us before you start working on something, so we can make sure you
### Compiling
#### Windows
**NOTE:** Don't open `CMakeLists.txt` from Visual Studio, as it won't generate files in the `build` directory.
Don't open `CMakeLists.txt` from Visual Studio, as it won't generate files in the `build` directory.
##### Prerequisites
1. [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2017 or later
1. [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2022
* C++ and C# desktop development
* Windows Universal CRT SDK
* C++ CMake tools for Windows
@ -98,9 +98,8 @@ Please contact us before you start working on something, so we can make sure you
2. `cd` to the Cxbx-Reloaded directory.
3. Run these commands.
1. `mkdir build & cd build`
2. `cmake .. -G "Visual Studio 16 2019" -A Win32`
* Visual Studio 2019 16.1 or later has CMake 3.14 bundled, and is required for the Visual Studio 2019 generator.
* Use `cmake .. -G "Visual Studio 15 2017" -A Win32` for Visual Studio 2017.
2. `cmake .. -G "Visual Studio 17 2022" -A Win32`
* VS2022 17.0 or later is required.
4. Open `Cxbx-Reloaded.sln` from the `build` directory.
5. Select the Release configuration, then click Build.
* Debug builds are **significantly slower, and only for developers**.
@ -115,3 +114,5 @@ You can support [Luke Usher](https://github.com/LukeUsher), initiator of Cxbx-Re
* All contributors to the original Cxbx and [Dxbx](https://github.com/PatrickvL/Dxbx) projects. Without them Cxbx-Reloaded would not exist at all.
* [XQEMU](https://github.com/xqemu/xqemu) - While the majority of Cxbx-R is our own work (Kernel, HLE, etc), the NV2A LLE and NVNet implementation are primarily the work of the XQEMU developers.
* [XboxDev](https://github.com/xboxdev) - Providing Xbox hardware research & useful tooling.
* [XbSymbolDatabase](https://github.com/Cxbx-Reloaded/XbSymbolDatabase) - Providing support to detect symbols across XDK builds from reverse engineered retail titles.
* [Xbox Kernel Test Suite](https://github.com/Cxbx-Reloaded/xbox_kernel_test_suite) - Making accurate tests on hardware to compare against cxbxr's kernel implementation.

2
import/SDL2 vendored

@ -1 +1 @@
Subproject commit 0e9560aea22818884921e5e5064953257bfe7fa7
Subproject commit fa24d868ac2f8fd558e4e914c9863411245db8fd

@ -1 +1 @@
Subproject commit 8b000c0ca7f20d88dddd95e80ad257ba2a0cffaa
Subproject commit 774111351210e6f340246d6fb32741b09708f381

2
import/cs_x86 vendored

@ -1 +1 @@
Subproject commit 5fb27bcf6745c35d8889e48ead4314022b8ec931
Subproject commit f8a95b7afa963c90b01a9e7cd758346f95c90f50

1
import/libusb vendored Submodule

@ -0,0 +1 @@
Subproject commit cf178f1fac38426990425cc034f7d4b8c9e1e388

1
import/mio vendored Submodule

@ -0,0 +1 @@
Subproject commit 3f86a95c0784d73ce6815237ec33ed25f233b643

1
import/nv2a_vsh_cpu vendored Submodule

@ -0,0 +1 @@
Subproject commit ead0af5dee49e408c005151393f8a7dbcd27026c

2
import/subhook vendored

@ -1 +1 @@
Subproject commit a86a253e6e764275a21ab7c9348f84d1f95e409f
Subproject commit 83d4e1ebef3588fae48b69a7352cc21801cb70bc

View file

@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
project(cxbx)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
# Suppress extra stuff from generated solution
set(CMAKE_SUPPRESS_REGENERATION true)
@ -12,9 +12,10 @@ include_directories(
"${CXBXR_ROOT_DIR}/src/common"
"${CXBXR_ROOT_DIR}/src/common/Win32"
"${CXBXR_ROOT_DIR}/import/OpenXDK/include"
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
"${CXBXR_ROOT_DIR}/import/distorm/include"
"${CXBXR_ROOT_DIR}/import/glew-2.0.0/include"
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
"${CXBXR_ROOT_DIR}/import/libusb/libusb"
"${CXBXR_ROOT_DIR}/import/simpleini"
"${CXBXR_ROOT_DIR}/import/winpcap/Include"
"${CXBXR_ROOT_DIR}/import/xxHash"
@ -41,6 +42,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
LTC_NO_PRNGS
LTC_NO_MISC
LTC_NO_PROTOTYPES
# Enable Chihiro work
CHIHIRO_WORK
)
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically
@ -77,14 +81,9 @@ file (GLOB RESOURCES
"${CXBXR_ROOT_DIR}/src/.editorconfig"
)
source_group(TREE ${CXBXR_ROOT_DIR}/import PREFIX import FILES
${CXBXR_HEADER_EMU_IMPORT}
)
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX header FILES
${CXBXR_HEADER_GUIv1}
${CXBXR_HEADER_COMMON}
${CXBXR_HEADER_EMU}
)
source_group(TREE ${CXBXR_ROOT_DIR}/import PREFIX import FILES
@ -94,7 +93,6 @@ source_group(TREE ${CXBXR_ROOT_DIR}/import PREFIX import FILES
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX source FILES
${CXBXR_SOURCE_GUIv1}
${CXBXR_SOURCE_COMMON}
${CXBXR_SOURCE_EMU}
)
source_group(TREE ${CXBXR_ROOT_DIR} FILES ${RESOURCES})
@ -102,12 +100,8 @@ source_group(TREE ${CXBXR_ROOT_DIR} FILES ${RESOURCES})
add_executable(cxbx WIN32 ${RESOURCES}
${CXBXR_HEADER_GUIv1}
${CXBXR_HEADER_COMMON}
${CXBXR_HEADER_EMU_IMPORT}
${CXBXR_HEADER_EMU}
${CXBXR_SOURCE_GUIv1}
${CXBXR_SOURCE_COMMON}
${CXBXR_SOURCE_EMU_IMPORT}
${CXBXR_SOURCE_EMU}
${CXBXR_GIT_VERSION_H}
)
@ -117,15 +111,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/linker-options
set_target_properties(cxbx PROPERTIES
LINK_FLAGS "
/INCREMENTAL:NO \
/LARGEADDRESSAWARE \
/FIXED \
/SAFESEH:NO \
/DYNAMICBASE:NO \
/BASE:0x10000 \
/STACK:65536,65536 \
/NODEFAULTLIB:libcmt \
/DELAYLOAD:wpcap.dll \
"
LINK_FLAGS_RELEASE "
/LTCG \
@ -135,7 +121,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically
# /Zi = create a PDB file without affecting optimization
# /Ob2 = Controls inline expansion of functions.
# /Ob3 = Controls inline expansion of functions.
# /Oi = Generate intrinsic functions
# /Ot = In favor of using fast code than small code
# /GL = Whole program optimization
@ -146,7 +132,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Set optimization options for release build
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
/Zi \
/Ob2 \
/Ob3 \
/Oi \
/Ot \
/GL \
@ -184,7 +170,7 @@ set(WINS_LIB
comctl32
XINPUT9_1_0
Iphlpapi
wpcap
Dwmapi
)
target_link_libraries(cxbx
@ -193,6 +179,8 @@ target_link_libraries(cxbx
libtomcrypt
SDL2
imgui
libusb
mio::mio_min_winapi
${WINS_LIB}
)

View file

@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
project(cxbxr-emu)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
# Suppress extra stuff from generated solution
set(CMAKE_SUPPRESS_REGENERATION true)
@ -15,9 +15,10 @@ include_directories(
"${CXBXR_ROOT_DIR}/src/common"
"${CXBXR_ROOT_DIR}/src/common/Win32"
"${CXBXR_ROOT_DIR}/import/OpenXDK/include"
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
"${CXBXR_ROOT_DIR}/import/distorm/include"
"${CXBXR_ROOT_DIR}/import/glew-2.0.0/include"
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
"${CXBXR_ROOT_DIR}/import/libusb/libusb"
"${CXBXR_ROOT_DIR}/import/simpleini"
"${CXBXR_ROOT_DIR}/import/winpcap/Include"
"${CXBXR_ROOT_DIR}/import/xxHash"
@ -47,6 +48,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Use inline XXHash version
XXH_INLINE_ALL
# Enable Chihiro work
CHIHIRO_WORK
)
add_compile_options(
/EHs
@ -127,7 +131,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Set optimization options for release build
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
/Zi \
/Ob2 \
/Ob3 \
/Oi \
/Ot \
/GL \
@ -169,6 +173,9 @@ target_link_libraries(cxbxr-emu
libtomcrypt
SDL2
imgui
libusb
nv2a_vsh_emulator
mio::mio_min_winapi
${WINS_LIB}
)

View file

@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.12)
project(cxbxr-ldr)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
# Suppress extra stuff from generated solution
set(CMAKE_SUPPRESS_REGENERATION true)

View file

@ -0,0 +1,51 @@
cmake_minimum_required (VERSION 3.8)
project(libusb LANGUAGES CXX)
# Since libusb doesn't have CMake, we'll make an interface project here.
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_compile_definitions(
_CRT_SECURE_NO_WARNINGS
_CRT_NONSTDC_NO_DEPRECATE
)
endif()
include_directories(
"${CXBXR_ROOT_DIR}/import/libusb/msvc"
"${CXBXR_ROOT_DIR}/import/libusb/libusb")
file (GLOB HEADERS
"${CXBXR_ROOT_DIR}/import/libusb/libusb/libusb.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/libusbi.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/version.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/version_nano.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/msvc/config.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/events_windows.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/threads_windows.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_common.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_usbdk.h"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_winusb.h"
)
file (GLOB SOURCES
"${CXBXR_ROOT_DIR}/import/libusb/libusb/core.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/descriptor.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/hotplug.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/io.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/strerror.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/sync.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/events_windows.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/threads_windows.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_common.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_usbdk.c"
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_winusb.c"
)
source_group(TREE ${CXBXR_ROOT_DIR}/import/libusb/libusb PREFIX header FILES ${HEADERS})
source_group(TREE ${CXBXR_ROOT_DIR}/import/libusb/libusb PREFIX source FILES ${SOURCES})
add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES})
target_include_directories(${PROJECT_NAME}
PUBLIC "${CXBXR_ROOT_DIR}/import/libusb"
)

View file

@ -27,14 +27,3 @@ message("Runtime Build Directory: ${TargetRunTimeDir}")
# Copy glew32.dll to build type's folder.
set(CXBXR_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")
file(COPY ${CXBXR_GLEW_DLL} DESTINATION ${TargetRunTimeDir})
# Copy certain HLSL files to the output directory, which we will load at runtime
set(CXBXR_HLSL_FILES
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
)
set(HlslOutputDir ${TargetRunTimeDir}/hlsl)
file(MAKE_DIRECTORY ${HlslOutputDir})
file(COPY ${CXBXR_HLSL_FILES} DESTINATION ${HlslOutputDir})

View file

@ -3,5 +3,5 @@ root = true
[*]
indent_size = 4
trim_trailing_whitespace = true
end_of_line = crlf
end_of_line = lf
insert_final_newline = true

View file

@ -61,13 +61,14 @@ enum DebugMode { DM_NONE, DM_CONSOLE, DM_FILE };
/*! debugger enable state */
enum DebuggerState { debuggerOff, debuggerOn };
/*! indicates emulation of an Chihiro (arcade, instead of Xbox console) executable */
/*! indicates emulation of a Chihiro system */
extern bool g_bIsChihiro;
/*! indicates emulation of a Debug xbe executable */
extern bool g_bIsDebug;
/*! indicates emulation of a DevKit system */
/* Note: Our DevKit emulation lacks the kernel debugging interface and virtual dvd-rom emulator card, so this is actually a Debug Kit */
extern bool g_bIsDevKit;
/*! indicates emulation of a Retail xbe executable*/
/*! indicates emulation of a Retail system */
extern bool g_bIsRetail;
/*! indicates ability to save on exit (needed for settings reset) */
@ -83,6 +84,4 @@ extern volatile bool g_bPrintfOn;
#define CxbxSetThreadName(Name)
#endif
#include <filesystem>
#endif

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
<supportedRuntime version="v4.8" sku=".NETFramework,Version=v4.8"/>
</startup>
</configuration>

View file

@ -13,6 +13,12 @@ add_compile_options(
/langversion:6
)
# First, we must define .NET Framework version before include cs_x86's projects.
# Which then will pass down version we want unified.
set(DOTNET_TARGET_FRAMEWORK_VERSION "v4.8")
add_subdirectory("${CXBXR_ROOT_DIR}/import/cs_x86" "${CMAKE_BINARY_DIR}/import/cs_x86")
set(CXBXR_DEBUGGER_SRC_DIR "${CXBXR_ROOT_DIR}/src/CxbxDebugger")
file (GLOB SOURCES
@ -135,10 +141,9 @@ set_target_properties(cxbxr-debugger PROPERTIES
VS_GLOBAL_ROOTNAMESPACE "CxbxDebugger"
DOTNET_TARGET_FRAMEWORK_VERSION ${DOTNET_TARGET_FRAMEWORK_VERSION}
)
set_property(TARGET cxbxr-debugger PROPERTY DOTNET_TARGET_FRAMEWORK_VERSION "v4.5")
target_link_libraries(cxbxr-debugger cs_x86)
install(TARGETS ${PROJECT_NAME}

View file

@ -7,8 +7,10 @@
/*! version string dependent on trace flag */
#ifndef _DEBUG_TRACE
const char* CxbxVersionStr = _GIT_VERSION " (" __DATE__ ")";
const char *CxbxrHashBuild = _GIT_VERSION;
#else
const char* CxbxVersionStr = _GIT_VERSION "-Trace (" __DATE__ ")";
const char *CxbxrHashBuild = _GIT_VERSION "-Trace";
#endif
static constexpr const char *GitVersionStr = _GIT_VERSION;

View file

@ -1,6 +1,7 @@
#pragma once
extern const char* CxbxVersionStr;
extern const char *CxbxrHashBuild;
// Note: GitVersionMaxLength should be large enough to accomodate the longest git version string we can practically expect to have. This is necessary
// to avoid possible mismatches in the string length which can happen if the user mixes different cxbxr versions

View file

@ -163,6 +163,14 @@ inline constexpr uint32_t FLASH_DEVICE4_END = (FLASH_DEVICE4_BASE - 1 + FLA
#define XBOX_MEMORY_SIZE (MiB(64))
#define CHIHIRO_MEMORY_SIZE (MiB(128))
// Common page calculations
#define ROUND_UP_4K(size) (((size) + PAGE_MASK) & (~PAGE_MASK))
#define ROUND_UP(size, alignment) (((size) + (alignment - 1)) & (~(alignment - 1)))
#define ROUND_DOWN_4K(size) ((size) & (~PAGE_MASK))
#define ROUND_DOWN(size, alignment) ((size) & (~(alignment - 1)))
#define CHECK_ALIGNMENT(size, alignment) (((size) % (alignment)) == 0)
#define PAGE_ALIGN(address) ROUND_DOWN_4K(address)
// Windows' address space allocation granularity;
// See https://blogs.msdn.microsoft.com/oldnewthing/20031008-00/?p=42223
const int BLOCK_SIZE = KiB(64);

View file

@ -27,8 +27,8 @@
#define LOG_PREFIX CXBXR_MODULE::EEPR
#define LOG_PREFIX_INIT CXBXR_MODULE::INIT
#include <core\kernel\exports\xboxkrnl.h> // For XC_VALUE_INDEX and XBOX_EEPROM
#include "cxbxr.hpp" // For CxbxrAbort
#include <stdio.h> // For printf
#include <shlobj.h> // For HANDLE, CreateFile, CreateFileMapping, MapViewOfFile
#include <random>
@ -36,7 +36,6 @@
#include "EmuEEPROM.h" // For EEPROMInfo, EEPROMInfos
#include "core\kernel\support\Emu.h" // For EmuWarning
#include "..\..\src\devices\LED.h" // For SetLEDSequence
#include "..\core\kernel\init\CxbxKrnl.h"
xbox::XBOX_EEPROM *EEPROM = nullptr; // Set using CxbxRestoreEEPROM()
@ -90,6 +89,7 @@ void gen_section_CRCs(xbox::XBOX_EEPROM* eeprom) {
);
}
#ifdef CXBXR_EMU
xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
{
xbox::XBOX_EEPROM *pEEPROM;
@ -141,10 +141,8 @@ xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
LARGE_INTEGER len_li;
GetFileSizeEx(hFileEEPROM, &len_li);
unsigned int FileSize = len_li.u.LowPart;
if (FileSize != 256)
{
CxbxKrnlCleanup("%s : EEPROM.bin file is not 256 bytes large!\n", __func__);
return nullptr;
if (FileSize != 256) {
CxbxrAbort("%s : EEPROM.bin file is not 256 bytes large!\n", __func__);
}
// Map EEPROM.bin contents into memory :
@ -194,6 +192,7 @@ xbox::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin)
return pEEPROM;
}
#endif
void EmuEEPROMReset(xbox::XBOX_EEPROM* eeprom)
{

187
src/common/FilePaths.cpp Normal file
View file

@ -0,0 +1,187 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx-Reloaded project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * All rights reserved
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::FILE
#define LOG_PREFIX_INIT CXBXR_MODULE::INIT
#include <filesystem>
#include "common/cxbxr.hpp"
#include "Settings.hpp"
#include "EmuShared.h"
#include "xxhash.h" // for XXH3_64bits
#include "core/kernel/common/xbox.h"
#include "Logging.h"
char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 };
char szFilePath_EEPROM_bin[MAX_PATH] = { 0 };
std::string g_DataFilePath;
std::string g_DiskBasePath;
std::string g_MuBasePath;
//TODO: Possible move CxbxResolveHostToFullPath inline function someplace else if become useful elsewhere.
// Let filesystem library clean it up for us, including resolve host's symbolic link path.
// Since internal kernel do translate to full path than preserved host symoblic link path.
void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence) {
std::error_code error;
std::filesystem::path sanityPath = std::filesystem::canonical(file_path, error);
if (error) {
// The MS implementation of std::filesystem::canonical internally calls GetFinalPathNameByHandleW, which fails with ERROR_FILE_NOT_FOUND when called
// on a file inside a mounted xiso under Windows with the xbox-iso-vfs tool because of this known dokany bug https://github.com/dokan-dev/dokany/issues/343
EmuLogInit(LOG_LEVEL::WARNING, "Could not resolve to %s: %s, dokany in use? The error was: %s",
finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
sanityPath = std::filesystem::absolute(std::filesystem::weakly_canonical(file_path, error), error);
if (error) {
CxbxrAbortEx(LOG_PREFIX_INIT, "Could not resolve to %s: %s. The error was: %s", finish_error_sentence.data(), file_path.string().c_str(), error.message().c_str());
}
}
file_path = sanityPath;
}
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence) {
std::filesystem::path sanityPath(file_path);
CxbxResolveHostToFullPath(sanityPath, finish_error_sentence);
file_path = sanityPath.string();
}
// NOTE: Do NOT modify g_<custom>BasePath variables after this call!
void CxbxrInitFilePaths()
{
if (g_Settings) {
g_DataFilePath = g_Settings->GetDataLocation();
}
else {
char dataLoc[MAX_PATH];
g_EmuShared->GetDataLocation(dataLoc);
g_DataFilePath = dataLoc;
}
// Make sure our data folder exists :
bool result = std::filesystem::exists(g_DataFilePath);
if (!result && !std::filesystem::create_directory(g_DataFilePath)) {
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded's data folder!", __func__);
}
// Make sure the EmuDisk folder exists
g_DiskBasePath = g_DataFilePath + "\\EmuDisk";
result = std::filesystem::exists(g_DiskBasePath);
if (!result && !std::filesystem::create_directory(g_DiskBasePath)) {
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__);
}
CxbxResolveHostToFullPath(g_DiskBasePath, "Cxbx-Reloaded's EmuDisk directory");
g_DiskBasePath = std::filesystem::path(g_DiskBasePath).append("").string();
// Make sure the EmuDMu folder exists
g_MuBasePath = g_DataFilePath + "\\EmuMu";
result = std::filesystem::exists(g_MuBasePath);
if (!result && !std::filesystem::create_directory(g_MuBasePath)) {
CxbxrAbort("%s : Couldn't create Cxbx-Reloaded EmuMu folder!", __func__);
}
CxbxResolveHostToFullPath(g_MuBasePath, "Cxbx-Reloaded's EmuMu directory");
g_MuBasePath = std::filesystem::path(g_MuBasePath).append("").string();
snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", g_DataFilePath.c_str());
GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH);
}
// Loads a keys.bin file as generated by dump-xbox
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
void LoadXboxKeys()
{
std::string keys_path = g_DataFilePath + "\\keys.bin";
// Attempt to open Keys.bin
FILE* fp = fopen(keys_path.c_str(), "rb");
if (fp != nullptr) {
// Determine size of Keys.bin
xbox::XBOX_KEY_DATA keys[2];
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
// If the size of Keys.bin is correct (two keys), read it
if (size == xbox::XBOX_KEY_LENGTH * 2) {
fread(keys, xbox::XBOX_KEY_LENGTH, 2, fp);
memcpy(xbox::XboxEEPROMKey, &keys[0], xbox::XBOX_KEY_LENGTH);
memcpy(xbox::XboxCertificateKey, &keys[1], xbox::XBOX_KEY_LENGTH);
}
else {
EmuLog(LOG_LEVEL::WARNING, "Keys.bin has an incorrect filesize. Should be %d bytes", xbox::XBOX_KEY_LENGTH * 2);
}
fclose(fp);
return;
}
// If we didn't already exit the function, keys.bin could not be loaded
EmuLog(LOG_LEVEL::WARNING, "Failed to load Keys.bin. Cxbx-Reloaded will be unable to read Save Data from a real Xbox");
}
static HANDLE hMapDataHash = nullptr;
bool CxbxrLockFilePath()
{
std::stringstream filePathHash("Local\\");
uint64_t hashValue = XXH3_64bits(g_DataFilePath.c_str(), g_DataFilePath.length() + 1);
if (!hashValue) {
CxbxrAbort("%s : Couldn't generate Cxbx-Reloaded's data folder hash!", __func__);
}
filePathHash << std::hex << hashValue;
hMapDataHash = CreateFileMapping(
INVALID_HANDLE_VALUE, // Paging file
nullptr, // default security attributes
PAGE_READONLY, // readonly access
0, // size: high 32 bits
/*Dummy size*/4, // size: low 32 bits
filePathHash.str().c_str() // name of map object
);
if (hMapDataHash == nullptr) {
return false;
}
if (GetLastError() == ERROR_ALREADY_EXISTS) {
PopupError(nullptr, "Data path directory is currently in use.\nUse a different data path directory or stop emulation from another process.");
CloseHandle(hMapDataHash);
return false;
}
return true;
}
void CxbxrUnlockFilePath()
{
// Close opened file path lockdown shared memory.
if (hMapDataHash) {
CloseHandle(hMapDataHash);
hMapDataHash = nullptr;
}
}

53
src/common/FilePaths.hpp Normal file
View file

@ -0,0 +1,53 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx-Reloaded project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
extern char szFilePath_CxbxReloaded_Exe[MAX_PATH];
extern char szFilePath_EEPROM_bin[MAX_PATH];
extern std::string g_DataFilePath;
extern std::string g_DiskBasePath;
extern std::string g_MuBasePath;
#include <filesystem>
//TODO: Possible move CxbxResolveHostToFullPath inline function someplace else if become useful elsewhere.
// Let filesystem library clean it up for us, including resolve host's symbolic link path.
// Since internal kernel do translate to full path than preserved host symoblic link path.
void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence);
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence);
// NOTE: Do NOT modify g_<custom>BasePath variables after this call!
void CxbxrInitFilePaths();
// Loads a keys.bin file as generated by dump-xbox
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
void LoadXboxKeys();
bool CxbxrLockFilePath();
void CxbxrUnlockFilePath();

View file

@ -49,8 +49,9 @@ void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value);
// ******************************************************************
typedef enum class _IPC_UPDATE_KERNEL {
CONFIG_LOGGING_SYNC = 0
, CONFIG_INPUT_SYNC
CONFIG_LOGGING_SYNC = 0,
CONFIG_INPUT_SYNC,
CONFIG_CHANGE_TIME
} IPC_UPDATE_KERNEL;
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd);

View file

@ -82,6 +82,7 @@ const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = {
"XMO ",
"RINP ",
"JVS ",
"LIBUSB ",
"KRNL ",
"LOG ",
"XBOX ",
@ -159,7 +160,7 @@ inline void EmuLogOutputEx(const CXBXR_MODULE cxbxr_module, const LOG_LEVEL leve
}
// print out a custom message to the console or kernel debug log file
void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...)
void EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...)
{
if (szWarningMessage == NULL) {
return;
@ -180,7 +181,7 @@ void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWa
}
}
void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...)
void EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...)
{
if (szWarningMessage == NULL) {
return;
@ -312,7 +313,7 @@ PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, con
uType |= MB_OKCANCEL;
break;
case PopupButtons::AbortRetryIgnore:
uType |= MB_RETRYCANCEL;
uType |= MB_ABORTRETRYIGNORE;
break;
case PopupButtons::YesNoCancel:
uType |= MB_YESNOCANCEL;
@ -328,11 +329,11 @@ PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, con
va_list argp;
va_start(argp, message);
// allocate predicted buffer size then write to buffer afterward.
std::vector<char> Buffer(1+std::vsnprintf(nullptr, 0, message, argp));
std::string Buffer(1+std::vsnprintf(nullptr, 0, message, argp), '\0');
vsnprintf(Buffer.data(), Buffer.size(), message, argp);
va_end(argp);
EmuLogOutputEx(cxbxr_module, level, "Popup : %s", Buffer);
EmuLogOutputEx(cxbxr_module, level, "Popup : %s", Buffer.c_str());
// If user is using exclusive fullscreen, we need to refrain all popups.
if (g_disablePopupMessages) {
@ -395,8 +396,13 @@ inline void output_wchar(std::ostream& os, wchar_t c)
default: os << "\\x" << std::setfill('0') << std::setw(4) << std::right << std::hex << std::uppercase << (wint_t)c;
}
}
else
os << c;
else {
const wchar_t *wc = reinterpret_cast<const wchar_t *>(&c);
std::string dst(2, '\0');
std::mbstate_t ps{};
std::wcsrtombs(dst.data(), &wc, 1, &ps);
os << dst;
}
}
LOG_SANITIZE_HEADER(hex1, uint8_t)

View file

@ -90,6 +90,7 @@ typedef enum class _CXBXR_MODULE: unsigned int {
XMO,
RINP,
JVS,
LIBUSB,
// kernel
KRNL,
LOG,
@ -121,8 +122,8 @@ extern std::atomic_int g_CurrentLogLevel;
extern std::atomic_bool g_CurrentLogPopupTestCase;
// print out a log message to the console or kernel debug log file if level is high enough
void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...);
void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...);
void EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...);
void EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...);
#define EmuLog(level, fmt, ...) EmuLogEx(LOG_PREFIX, level, fmt, ##__VA_ARGS__)
@ -224,9 +225,30 @@ extern inline void output_wchar(std::ostream& os, wchar_t c);
// By default, sanitization functions simply return the given argument
// (type and value) which results in calls to standard output writers.
template<class T>
inline T _log_sanitize(T value, int ignored_length = 0)
inline auto _log_sanitize(T value, int ignored_length = 0)
{
// This is necessary because C++20 has deleted the overloaded operator<< of ostream for wchar_t, char8_t, char16_t and char32_t.
// The proper solution is to use wide strings in all our logging macros/functions, see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/743
if constexpr (std::is_same_v<xbox::wchar_xt, std::remove_cvref_t<std::remove_pointer_t<T>>>) {
if constexpr (std::is_pointer_v<T>) {
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(value);
std::size_t len = std::wcslen(wstr);
std::string dst(len + 1, '\0');
std::mbstate_t ps{};
std::wcsrtombs(dst.data(), &wstr, len, &ps);
return dst;
}
else {
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(&value);
std::string dst(2, '\0');
std::mbstate_t ps{};
std::wcsrtombs(dst.data(), &wstr, 1, &ps);
return dst;
}
}
else {
return value;
}
}
#if 0 // TODO FIXME : Disabled for now, as this is incorrectly called for INT types too
@ -293,8 +315,8 @@ constexpr const char* remove_prefix(const char* str, const char *prefix) {
return (str_skip_prefix(str, prefix) == str + str_length(prefix)) ? str_skip_prefix(str, prefix) : str;
}
static const char* xbox_prefix = "xbox::";
static const char* emupatch_prefix = "EmuPatch_"; // See #define EMUPATCH
static constexpr const char* xbox_prefix = "xbox::";
static constexpr const char* emupatch_prefix = "EmuPatch_"; // See #define EMUPATCH
constexpr const char* remove_emupatch_prefix(const char* str) {
// return an empty string when str isn't given

View file

@ -319,12 +319,6 @@ bool isSystemFlagSupport(unsigned int reserved_systems, unsigned int assign_syst
if (reserved_systems & assign_system) {
return true;
}
// TODO: Once host's standalone emulation is remove from GUI, remove below as well.
#ifndef CXBXR_EMU
if (reserved_systems == 0) {
return true;
}
#endif
return false;
}

View file

@ -1,55 +0,0 @@
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx 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 Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2016 Patrick van Logchem <pvanlogchem@gmail.com>
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef RESERVEDMEMORY_H
#define RESERVEDMEMORY_H
#ifndef CXBXR_EMU
#if defined(__cplusplus)
extern "C"
{
#endif
#include "EmuShared.h" // For XBE_MAX_VA, XBE_IMAGE_BASE and CXBX_BASE_OF_CODE
// The following code reserves virtual addresses from 0x00011000 upwards;
#define VM_PLACEHOLDER_SIZE (XBE_MAX_VA - XBE_IMAGE_BASE - CXBX_BASE_OF_CODE)
// First, declare the '.text' section again :
#pragma section(".text") // Note : 'read,write,execute' would cause a warning
// Then place the following variable into the '.text' section :
__declspec(allocate(".text"))
// This variable *MUST* be this large, for it to take up address space
// so that all other code and data in this module are placed outside of the
// maximum virtual memory range.
unsigned char virtual_memory_placeholder[VM_PLACEHOLDER_SIZE]; // = { OPCODE_NOP_90 };
// TODO : Try to get the same result without enlarging our executable by 128 MB!
#if defined(__cplusplus)
}
#endif
#endif
#endif // RESERVEDMEMORY_H

View file

@ -52,7 +52,12 @@ static_assert(false, "Please implement support for cross-platform's user profile
uint16_t g_LibVersion_D3D8 = 0;
uint16_t g_LibVersion_DSOUND = 0;
// NOTE: Update settings_version when add/edit/delete setting's structure.
// NOTE: Update settings_version when conversion to setting's structure is required.
// UPDATE: When settings are removed, use "if (use false && settings_version < {next_version}) {" statement
// until existing settings require replacement or conversion. next_version input is a hardcode number.
// Settings version 10 and later should consider as not backward compatible.
// TODO: Add read-only state when using an older build and add a notification for will not be able save to file.
// The sooner we do this, the better before version upgrade.
///////////////////////////
// * History:
// * 2: (RadWolfie), initial version
@ -96,7 +101,6 @@ static struct {
const char* AllowAdminPrivilege = "AllowAdminPrivilege";
const char* LoggedModules = "LoggedModules";
const char* LogLevel = "LogLevel";
const char* LoaderExecutable = "LoaderExecutable";
const char* LogPopupTestCase = "LogPopupTestCase";
} sect_core_keys;
@ -113,8 +117,11 @@ static struct {
static const char* section_overlay = "overlay";
static struct {
const char* build_hash = "Build Hash";
const char* FPS = "FPS";
const char* hle_lle_stats = "HLE/LLE Stats";
const char* title_name = "Title Name";
const char* file_name = "File Name";
} sect_overlay_keys;
static const char* section_audio = "audio";
@ -378,8 +385,6 @@ bool Settings::LoadConfig()
}
m_core.bLogPopupTestCase = m_si.GetBoolValue(section_core, sect_core_keys.LogPopupTestCase, /*Default=*/true);
m_core.bUseLoaderExec = m_si.GetBoolValue(section_core, sect_core_keys.LoaderExecutable, /*Default=*/true);
// ==== Core End ============
// Delete/update legacy configs from previous revisions
@ -496,7 +501,7 @@ bool Settings::LoadConfig()
continue;
}
auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
const auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
for (int i = 0; i < num_buttons; i++) {
char control_name[XBOX_BUTTON_NAME_LENGTH];
std::sprintf(control_name, sect_input_profiles.control, ctrl_names[i]);
@ -516,6 +521,10 @@ bool Settings::LoadConfig()
lambda(dev_num_buttons[device], button_sbc_names);
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
lambda(dev_num_buttons[device], button_lightgun_names);
break;
}
}
@ -541,8 +550,11 @@ bool Settings::LoadConfig()
// ==== Overlay Begin =========
m_overlay.build_hash = m_si.GetBoolValue(section_overlay, sect_overlay_keys.build_hash, false);
m_overlay.fps = m_si.GetBoolValue(section_overlay, sect_overlay_keys.FPS, false);
m_overlay.hle_lle_stats = m_si.GetBoolValue(section_overlay, sect_overlay_keys.hle_lle_stats, false);
m_overlay.title_name = m_si.GetBoolValue(section_overlay, sect_overlay_keys.title_name, false);
m_overlay.file_name = m_si.GetBoolValue(section_overlay, sect_overlay_keys.file_name, false);
// ==== Overlay End ===========
@ -597,8 +609,6 @@ bool Settings::Save(std::string file_path)
}
m_si.SetBoolValue(section_core, sect_core_keys.LogPopupTestCase, m_core.bLogPopupTestCase, nullptr, true);
m_si.SetBoolValue(section_core, sect_core_keys.LoaderExecutable, m_core.bUseLoaderExec, nullptr, true);
// ==== Core End ============
// ==== Video Begin =========
@ -668,7 +678,7 @@ bool Settings::Save(std::string file_path)
continue;
}
auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
const auto &lambda = [&control_names, &device](int num_buttons, const char *const ctrl_names[]) {
for (int i = 0; i < num_buttons; i++) {
char control_name[XBOX_BUTTON_NAME_LENGTH];
std::sprintf(control_name, sect_input_profiles.control, ctrl_names[i]);
@ -688,6 +698,9 @@ bool Settings::Save(std::string file_path)
lambda(dev_num_buttons[device], button_sbc_names);
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
lambda(dev_num_buttons[device], button_lightgun_names);
break;
}
}
@ -728,8 +741,11 @@ bool Settings::Save(std::string file_path)
// ==== Overlay Begin =======
m_si.SetBoolValue(section_overlay, sect_overlay_keys.build_hash, m_overlay.build_hash, nullptr, true);
m_si.SetBoolValue(section_overlay, sect_overlay_keys.FPS, m_overlay.fps, nullptr, true);
m_si.SetBoolValue(section_overlay, sect_overlay_keys.hle_lle_stats, m_overlay.hle_lle_stats, nullptr, true);
m_si.SetBoolValue(section_overlay, sect_overlay_keys.title_name, m_overlay.title_name, nullptr, true);
m_si.SetBoolValue(section_overlay, sect_overlay_keys.file_name, m_overlay.file_name, nullptr, true);
// ==== Overlay End =========
@ -785,8 +801,9 @@ void Settings::SyncToEmulator()
g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_BOTTOM], i, SLOT_BOTTOM);
if (m_input_port[i].Type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
g_EmuShared->SetInputDevNameSettings(m_input_port[i].DeviceName.c_str(), i);
if (m_input_port[i].Type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)) {
auto it = std::find_if(m_input_profiles[m_input_port[i].Type].begin(),
m_input_profiles[m_input_port[i].Type].end(), [this, i](const auto& profile) {
m_input_profiles[m_input_port[i].Type].end(), [this, i](const auto &profile) {
if (profile.ProfileName == m_input_port[i].ProfileName) {
return true;
}
@ -801,6 +818,7 @@ void Settings::SyncToEmulator()
}
}
}
}
// register Input general settings
g_EmuShared->SetInputGeneralSettings(&m_input_general);
@ -809,7 +827,7 @@ void Settings::SyncToEmulator()
g_EmuShared->SetHackSettings(&m_hacks);
// register data location setting
g_EmuShared->SetStorageLocation(GetDataLocation().c_str());
g_EmuShared->SetDataLocation(GetDataLocation().c_str());
// reset title mount path
g_EmuShared->SetTitleMountPath("");
@ -991,7 +1009,7 @@ void Settings::RemoveLegacyConfigs(unsigned int CurrentRevision)
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
std::string device_name = m_si.GetValue(current_section.c_str(), sect_input_port.device, "");
if (StrEndsWith(device_name, kb_str)) {
if (device_name.ends_with(kb_str)) {
device_name += "Mouse";
m_si.SetValue(current_section.c_str(), sect_input_port.device, device_name.c_str(), nullptr, true);
}
@ -1018,4 +1036,9 @@ void Settings::RemoveLegacyConfigs(unsigned int CurrentRevision)
if(CurrentRevision < 9) {
m_si.Delete(section_video, "HardwareYUV", true);
}
// see settings_version for details.
if(false && CurrentRevision < 10) {
m_si.Delete(section_core, "LoaderExecutable", true);
}
}

View file

@ -26,7 +26,6 @@
// ******************************************************************
#ifndef SETTINGS_HPP
#define SETTINGS_HPP
#include "Cxbx.h"
#include "SimpleIni.h"
#include "common\input\InputManager.h"
@ -58,7 +57,7 @@ typedef enum _CXBX_DATA {
// ******************************************************************
// * Define number of integers required to store logging settings
// ******************************************************************
#define NUM_INTEGERS_LOG 2
#define NUM_INTEGERS_LOG 3
enum {
LLE_NONE = 0,
@ -103,13 +102,13 @@ public:
char szStorageLocation[xbox::max_path] = "";
unsigned int LoggedModules[NUM_INTEGERS_LOG];
int LogLevel = 1;
bool bUseLoaderExec;
bool bUnused_WasUseLoaderExec;
bool allowAdminPrivilege;
bool bLogPopupTestCase;
bool Reserved4 = 0;
int Reserved99[10] = { 0 };
} m_core;
static_assert(sizeof(s_core) == 0x24C, assert_check_shared_memory(s_core));
static_assert(sizeof(s_core) == 0x250, assert_check_shared_memory(s_core));
// Video settings
struct s_video {

View file

@ -25,18 +25,45 @@
// *
// ******************************************************************
#ifdef _WIN32
#include <core\kernel\exports\xboxkrnl.h>
#include <windows.h>
#endif
#include <thread>
#include <vector>
#include <mutex>
#include <array>
#include "Timer.h"
#include "common\util\CxbxUtil.h"
#include "core\kernel\init\CxbxKrnl.h"
#ifdef __linux__
#include <time.h>
#endif
#include "core\kernel\support\EmuFS.h"
#include "core\kernel\exports\EmuKrnlPs.hpp"
#include "core\kernel\exports\EmuKrnl.h"
#include "devices\Xbox.h"
#include "devices\usb\OHCI.h"
#include "core\hle\DSOUND\DirectSound\DirectSoundGlobal.hpp"
static std::atomic_uint64_t last_qpc; // last time when QPC was called
static std::atomic_uint64_t exec_time; // total execution time in us since the emulation started
static uint64_t pit_last; // last time when the pit time was updated
static uint64_t pit_last_qpc; // last QPC time of the pit
// The frequency of the high resolution clock of the host, and the start time
int64_t HostQPCFrequency, HostQPCStartTime;
void timer_init()
{
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER *>(&HostQPCFrequency));
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&HostQPCStartTime));
pit_last_qpc = last_qpc = HostQPCStartTime;
pit_last = get_now();
// Synchronize xbox system time with host time
LARGE_INTEGER HostSystemTime;
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
xbox::KeSystemTime.High2Time = HostSystemTime.u.HighPart;
xbox::KeSystemTime.LowPart = HostSystemTime.u.LowPart;
xbox::KeSystemTime.High1Time = HostSystemTime.u.HighPart;
}
// More precise sleep, but with increased CPU usage
void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
@ -68,172 +95,94 @@ void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
}
}
// Virtual clocks will probably become useful once LLE CPU is implemented, but for now we don't need them.
// See the QEMUClockType QEMU_CLOCK_VIRTUAL of XQEMU for more info.
#define CLOCK_REALTIME 0
//#define CLOCK_VIRTUALTIME 1
// Vector storing all the timers created
static std::vector<TimerObject*> TimerList;
// The frequency of the high resolution clock of the host
uint64_t HostClockFrequency;
// Lock to acquire when accessing TimerList
std::mutex TimerMtx;
// Returns the current time of the timer
uint64_t GetTime_NS(TimerObject* Timer)
// NOTE: the pit device is not implemented right now, so we put this here
static uint64_t pit_next(uint64_t now)
{
#ifdef _WIN32
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
uint64_t Ret = Muldiv64(li.QuadPart, SCALE_S_IN_NS, (uint32_t)HostClockFrequency);
#elif __linux__
static struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
uint64_t Ret = Muldiv64(ts.tv_sec, SCALE_S_IN_NS, 1) + ts.tv_nsec;
#else
#error "Unsupported OS"
#endif
return Ret;
constexpr uint64_t pit_period = 1000;
uint64_t next = pit_last + pit_period;
if (now >= next) {
xbox::KiClockIsr(now - pit_last);
pit_last = get_now();
return pit_period;
}
return pit_last + pit_period - now; // time remaining until next clock interrupt
}
// Calculates the next expire time of the timer
static inline uint64_t GetNextExpireTime(TimerObject* Timer)
static void update_non_periodic_events()
{
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load();
// update dsound
dsound_worker();
// check for hw interrupts
for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) {
// If the interrupt is pending and connected, process it
if (g_bEnableAllInterrupts && HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) {
HalSystemInterrupts[i].Trigger(EmuInterruptList[i]);
}
}
}
// Deallocates the memory of the timer
void Timer_Destroy(TimerObject* Timer)
uint64_t get_now()
{
unsigned int index, i;
std::lock_guard<std::mutex>lock(TimerMtx);
index = TimerList.size();
for (i = 0; i < index; i++) {
if (Timer == TimerList[i]) {
index = i;
}
}
assert(index != TimerList.size());
delete Timer;
TimerList.erase(TimerList.begin() + index);
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
uint64_t elapsed_us = now.QuadPart - last_qpc;
last_qpc = now.QuadPart;
elapsed_us *= 1000000;
elapsed_us /= HostQPCFrequency;
exec_time += elapsed_us;
return exec_time;
}
// Thread that runs the timer
void ClockThread(TimerObject* Timer)
static uint64_t get_next(uint64_t now)
{
uint64_t NewExpireTime;
std::array<uint64_t, 5> next = {
pit_next(now),
g_NV2A->vblank_next(now),
g_NV2A->ptimer_next(now),
g_USB0->m_HostController->OHCI_next(now),
dsound_next(now)
};
return *std::min_element(next.begin(), next.end());
}
if (!Timer->Name.empty()) {
CxbxSetThreadName(Timer->Name.c_str());
}
if (Timer->IsXboxTimer) {
InitXboxThread();
g_AffinityPolicy->SetAffinityXbox();
} else {
g_AffinityPolicy->SetAffinityOther();
}
xbox::void_xt NTAPI system_events(xbox::PVOID arg)
{
// Testing shows that, if this thread has the same priority of the other xbox threads, it can take tens, even hundreds of ms to complete a single loop.
// So we increase its priority to above normal, so that it scheduled more often
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
NewExpireTime = GetNextExpireTime(Timer);
// Always run this thread at dpc level to prevent it from ever executing APCs/DPCs
xbox::KeRaiseIrqlToDpcLevel();
while (true) {
if (GetTime_NS(Timer) > NewExpireTime) {
if (Timer->Exit.load()) {
Timer_Destroy(Timer);
return;
const uint64_t last_time = get_now();
const uint64_t nearest_next = get_next(last_time);
while (true) {
update_non_periodic_events();
uint64_t elapsed_us = get_now() - last_time;
if (elapsed_us >= nearest_next) {
break;
}
Timer->Callback(Timer->Opaque);
NewExpireTime = GetNextExpireTime(Timer);
std::this_thread::yield();
}
Sleep(1); // prevent burning the cpu
}
}
// Changes the expire time of a timer
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms)
int64_t Timer_GetScaledPerformanceCounter(int64_t Period)
{
Timer->ExpireTime_MS.store(Expire_ms);
LARGE_INTEGER currentQPC;
QueryPerformanceCounter(&currentQPC);
// Scale frequency with overflow avoidance, like in std::chrono
// https://github.com/microsoft/STL/blob/6d2f8b0ed88ea6cba26cc2151f47f678442c1663/stl/inc/chrono#L703
const int64_t currentTime = currentQPC.QuadPart - HostQPCStartTime;
const int64_t whole = (currentTime / HostQPCFrequency) * Period;
const int64_t part = (currentTime % HostQPCFrequency) * Period / HostQPCFrequency;
return whole + part;
}
// Destroys the timer
void Timer_Exit(TimerObject* Timer)
{
Timer->Exit.store(true);
}
// Allocates the memory for the timer object
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer)
{
std::lock_guard<std::mutex>lock(TimerMtx);
TimerObject* pTimer = new TimerObject;
pTimer->Type = CLOCK_REALTIME;
pTimer->Callback = Callback;
pTimer->ExpireTime_MS.store(0);
pTimer->Exit.store(false);
pTimer->Opaque = Arg;
pTimer->Name = Name.empty() ? "Unnamed thread" : std::move(Name);
pTimer->IsXboxTimer = IsXboxTimer;
TimerList.emplace_back(pTimer);
return pTimer;
}
// Starts the timer
// Expire_MS must be expressed in NS
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
{
Timer->ExpireTime_MS.store(Expire_MS);
std::thread(ClockThread, Timer).detach();
}
// Retrives the frequency of the high resolution clock of the host
void Timer_Init()
{
#ifdef _WIN32
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
HostClockFrequency = freq.QuadPart;
#elif __linux__
ClockFrequency = 0;
#else
#error "Unsupported OS"
#endif
}
// ******************************************************************
void ScaledPerformanceCounter::Reset(uint32_t frequency)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_frequencyFactor = Muldiv64(HostClockFrequency, SCALE_S_IN_NS, frequency);
LARGE_INTEGER tsc;
QueryPerformanceCounter(&tsc);
m_lastQPC = tsc.QuadPart;
m_currentCount = 0;
m_currentRemainder = 0;
}
uint64_t ScaledPerformanceCounter::Tick()
{
std::lock_guard<std::mutex> lock(m_mutex);
LARGE_INTEGER qpc;
QueryPerformanceCounter(&qpc);
int64_t lastQpc = std::exchange(m_lastQPC, qpc.QuadPart);
qpc.QuadPart -= lastQpc;
qpc.QuadPart *= SCALE_S_IN_NS;
qpc.QuadPart += m_currentRemainder;
uint64_t quotient = qpc.QuadPart / m_frequencyFactor;
uint64_t remainder = qpc.QuadPart % m_frequencyFactor;
m_currentRemainder = remainder;
return m_currentCount += quotient;
}

View file

@ -40,49 +40,12 @@
#define SCALE_MS_IN_US 1000
#define SCALE_US_IN_US 1
/* typedef of the timer object and the callback function */
typedef void(*TimerCB)(void*);
typedef struct _TimerObject
{
int Type; // timer type
std::atomic_uint64_t ExpireTime_MS; // when the timer expires (ms)
std::atomic_bool Exit; // indicates that the timer should be destroyed
TimerCB Callback; // function to call when the timer expires
void* Opaque; // opaque argument to pass to the callback
std::string Name; // the name of the timer thread (if any)
bool IsXboxTimer; // indicates that the timer should run on the Xbox CPU
}
TimerObject;
extern uint64_t HostClockFrequency;
/* Timer exported functions */
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer);
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS);
void Timer_Exit(TimerObject* Timer);
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
uint64_t GetTime_NS(TimerObject* Timer);
void Timer_Init();
extern int64_t HostQPCFrequency;
void timer_init();
uint64_t get_now();
int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
void SleepPrecise(std::chrono::steady_clock::time_point targetTime);
// A stateful replacement for QueryPerformanceCounter, ticking at an arbitrary frequency
// Thread-safe and designed to avoid overflows at all cost
class ScaledPerformanceCounter
{
public:
ScaledPerformanceCounter() = default;
void Reset(uint32_t frequency);
uint64_t Tick();
private:
std::mutex m_mutex;
uint64_t m_frequencyFactor;
int64_t m_lastQPC;
uint64_t m_currentCount;
uint64_t m_currentRemainder;
};
#endif

169
src/common/cxbxr.cpp Normal file
View file

@ -0,0 +1,169 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx-Reloaded project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * All rights reserved
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::FILE
#ifdef CXBXR_EMU
#include "core/kernel/support/EmuFile.h" // For g_io_mu_metadata
#include "common/Timer.h" // For Timer_Shutdown
#include "core/common/video/RenderBase.hpp" // For g_renderbase
#include "core/kernel/memory-manager/VMManager.h"
extern void CxbxrKrnlSuspendThreads();
#endif
#include "cxbxr.hpp"
#include "EmuShared.h"
#include "Settings.hpp"
#include "Logging.h"
#include "win32/WineEnv.h"
#include "Settings.hpp"
volatile bool g_bPrintfOn = true;
bool CreateSettings()
{
g_Settings = new Settings();
if (g_Settings == nullptr) {
PopupError(nullptr, szSettings_alloc_error);
return false;
}
if (!g_Settings->Init()) {
return false;
}
log_get_settings();
return true;
}
bool HandleFirstLaunch()
{
bool bFirstLaunch;
g_EmuShared->GetIsFirstLaunch(&bFirstLaunch);
/* check if process is launch with elevated access then prompt for continue on or not. */
if (!bFirstLaunch) {
if (!CreateSettings()) {
return false;
}
// Wine will always run programs as administrator by default, it can be safely disregard.
// Since Wine doesn't use root permission. Unless user is running Wine as root.
bool bElevated = CxbxrIsElevated();
if (bElevated && !isWineEnv() && !g_Settings->m_core.allowAdminPrivilege) {
PopupReturn ret = PopupWarningEx(nullptr, PopupButtons::YesNo, PopupReturn::No,
"Cxbx-Reloaded has detected that it has been launched with Administrator rights.\n"
"\nThis is dangerous, as a maliciously modified Xbox titles could take control of your system.\n"
"\nAre you sure you want to continue?");
if (ret != PopupReturn::Yes) {
return false;
}
}
g_EmuShared->SetIsFirstLaunch(true);
}
return true;
}
[[noreturn]] void CxbxrShutDown(bool is_reboot)
{
if (!is_reboot) {
// Clear all kernel boot flags. These (together with the shared memory) persist until Cxbx-Reloaded is closed otherwise.
int BootFlags = 0;
g_EmuShared->SetBootFlags(&BootFlags);
}
// NOTE: This causes a hang when exiting while NV2A is processing
// This is okay for now: It won't leak memory or resources since TerminateProcess will free everything
// delete g_NV2A; // TODO : g_pXbox
// Shutdown the input device manager
g_InputDeviceManager.Shutdown();
#ifdef CXBXR_EMU
// NOTE: this code causes freezes/crashes at shutdown, so avoid for now
// This is very important process to prevent false positive report and allow IDEs to continue debug multiple reboots.
//CxbxrKrnlSuspendThreads();
if (g_io_mu_metadata) {
delete g_io_mu_metadata;
g_io_mu_metadata = nullptr;
}
// Shutdown the render manager
if (g_renderbase != nullptr) {
g_renderbase->Shutdown();
g_renderbase = nullptr;
}
// NOTE: Require to be after g_renderbase's shutdown process.
// NOTE: Must be last step of shutdown process and before CxbxUnlockFilePath call!
// Shutdown the memory manager
g_VMManager.Shutdown();
CxbxrUnlockFilePath();
if (CxbxKrnl_hEmuParent != NULL && !is_reboot) {
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0);
}
#endif
EmuShared::Cleanup();
TerminateProcess(GetCurrentProcess(), 0);
}
[[noreturn]] void CxbxrAbortEx(CXBXR_MODULE cxbxr_module, const char* szErrorMessage, ...)
{
// print out error message (if exists)
if (szErrorMessage != NULL)
{
char szBuffer2[1024];
va_list argp;
va_start(argp, szErrorMessage);
vsprintf(szBuffer2, szErrorMessage, argp);
va_end(argp);
(void)PopupCustomEx(nullptr, cxbxr_module, LOG_LEVEL::FATAL, PopupIcon::Error, PopupButtons::Ok, PopupReturn::Ok, "Received Fatal Message:\n\n* %s\n", szBuffer2); // Will also EmuLogEx
}
EmuLogInit(LOG_LEVEL::INFO, "MAIN: Terminating Process");
fflush(stdout);
// cleanup debug output
{
FreeConsole();
char buffer[16];
if (GetConsoleTitle(buffer, 16) != NULL)
freopen("nul", "w", stdout);
}
CxbxrShutDown();
}

58
src/common/cxbxr.hpp Normal file
View file

@ -0,0 +1,58 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx-Reloaded project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
#include <string>
#include <optional>
#include "Logging.h"
bool CreateSettings();
bool HandleFirstLaunch();
// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths.
void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence);
// Loads a keys.bin file as generated by dump-xbox
// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c
void LoadXboxKeys();
bool CxbxrLockFilePath();
void CxbxrUnlockFilePath();
// Hybrid functions depending on specific platforms
bool CxbxrIsElevated();
std::optional<std::string> CxbxrExec(bool useDebugger, void** hProcess, bool requestHandleProcess);
/*! cleanup emulation */
[[noreturn]] void CxbxrAbortEx(CXBXR_MODULE cxbxr_module, const char* szErrorMessage, ...);
#define CxbxrAbort(fmt, ...) CxbxrAbortEx(LOG_PREFIX, fmt, ##__VA_ARGS__)
/*! terminate gracefully the emulation */
[[noreturn]] void CxbxrShutDown(bool is_reboot = false);

View file

@ -51,16 +51,17 @@ void Button::GetText(char* const text, size_t size) const
SendMessage(m_button_hwnd, WM_GETTEXT, size, reinterpret_cast<LPARAM>(text));
}
void Button::AddTooltip(HWND hwnd, HWND tooltip_hwnd, char *text) const
void Button::AddTooltip(HWND hwnd, HWND tooltip_hwnd, std::string_view text) const
{
assert((hwnd != NULL) && (tooltip_hwnd != NULL));
std::string tooltip_text(text);
TOOLINFO tool = { 0 };
tool.cbSize = sizeof(tool);
tool.hwnd = hwnd;
tool.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
tool.uId = reinterpret_cast<UINT_PTR>(m_button_hwnd);
tool.lpszText = text;
tool.lpszText = tooltip_text.data();
SendMessage(tooltip_hwnd, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&tool));
}
@ -78,7 +79,6 @@ LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA
Button *button = reinterpret_cast<Button *>(dwRefData);
if (wParam & MK_SHIFT) {
static_cast<DukeInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
static_cast<DukeInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_SWAP);
}
else if (!(wParam & ~MK_RBUTTON)) {
button->ClearText();
@ -107,15 +107,31 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
case WM_RBUTTONDOWN: {
Button *button = reinterpret_cast<Button *>(dwRefData);
if (wParam & MK_SHIFT) {
static_cast<SbcInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_SWAP);
}
else if (!(wParam & ~MK_RBUTTON)) {
button->ClearText();
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
}
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
// Remove the window subclass when this window is destroyed
case WM_NCDESTROY: {
RemoveWindowSubclass(hWnd, ButtonLightgunSubclassProc, uIdSubclass);
}
break;
case WM_RBUTTONDOWN: {
Button *button = reinterpret_cast<Button *>(dwRefData);
button->ClearText();
static_cast<LightgunInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
}
break;
}

View file

@ -31,6 +31,7 @@
#include <Commctrl.h>
#include <string>
#define LIGHTGUN_NUM_BUTTONS 17
#define XBOX_CTRL_NUM_BUTTONS 25
#define SBC_NUM_BUTTONS 56
#define HIGHEST_NUM_BUTTONS SBC_NUM_BUTTONS
@ -50,7 +51,7 @@ public:
int GetId() const { return m_id; }
int GetIndex() const { return m_index; }
void *GetWnd() const { return m_wnd; }
void AddTooltip(HWND hwnd, HWND tooltip_hwnd, char *text) const;
void AddTooltip(HWND hwnd, HWND tooltip_hwnd, std::string_view text) const;
private:
@ -62,3 +63,4 @@ private:
LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

View file

@ -37,6 +37,7 @@
#include "DInputKeyboardMouse.h"
#include "InputManager.h"
#include "core\kernel\support\Emu.h"
#include <algorithm>
// Unfortunately, sdl doesn't seem to be able to capture keyboard/mouse input from windows it didn't create (we currently use
// win32 for that). So unless we create sdl windows, we will have to keep dinput around to handle keyboard/mouse input.

View file

@ -25,13 +25,15 @@
// *
// ******************************************************************
#include <algorithm>
#include "Button.h"
#include "InputManager.h"
#include "layout_xbox_device.h"
#include "gui/resource/ResCxbx.h"
static char *tooltip_text = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
static const char *tooltip_text_toggle = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
static const char *tooltip_text_no_toggle = "Left-click: start input detection\nRight-click: clear binding";
EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
{
@ -45,7 +47,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): {
for (size_t i = 0; i < ARRAY_SIZE(button_xbox_ctrl_id); i++) {
m_buttons.push_back(new Button(button_xbox_ctrl_id[i], i, hwnd, wnd));
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text);
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
// Install the subclass for the button control
SetWindowSubclass(GetDlgItem(hwnd, button_xbox_ctrl_id[i]), ButtonDukeSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
@ -56,7 +58,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
for (size_t i = 0; i < ARRAY_SIZE(button_sbc_id); i++) {
m_buttons.push_back(new Button(button_sbc_id[i], i, hwnd, wnd));
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text);
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_no_toggle);
// Install the subclass for the button control
SetWindowSubclass(GetDlgItem(hwnd, button_sbc_id[i]), ButtonSbcSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
@ -64,6 +66,17 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
}
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN): {
for (size_t i = 0; i < ARRAY_SIZE(button_lightgun_id); i++) {
m_buttons.push_back(new Button(button_lightgun_id[i], i, hwnd, wnd));
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_no_toggle);
// Install the subclass for the button control
SetWindowSubclass(GetDlgItem(hwnd, button_lightgun_id[i]), ButtonLightgunSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
}
}
break;
}
}
@ -120,3 +133,6 @@ void EmuDevice::CreateTooltipWindow()
SendMessage(m_tooltip_hwnd, TTM_SETMAXTIPWIDTH, 0, 500);
SendMessage(m_tooltip_hwnd, TTM_SETDELAYTIME, TTDT_AUTOPOP, 15000);
}
template void EmuDevice::BindDefault(const std::array<const char*, XBOX_CTRL_NUM_BUTTONS>& arr);
template void EmuDevice::BindDefault(const std::array<const char *, LIGHTGUN_NUM_BUTTONS> &arr);

View file

@ -53,4 +53,5 @@ private:
HWND m_tooltip_hwnd;
};
template void EmuDevice::BindDefault(const std::array<const char *, XBOX_CTRL_NUM_BUTTONS> &arr);
extern template void EmuDevice::BindDefault(const std::array<const char *, XBOX_CTRL_NUM_BUTTONS> &arr);
extern template void EmuDevice::BindDefault(const std::array<const char *, LIGHTGUN_NUM_BUTTONS> &arr);

View file

@ -53,8 +53,8 @@ std::string GetInputDeviceName(int dev_type)
str = "MS Gamepad S";
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
str = "Light gun";
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
str = "EMS TopGun II";
break;
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
@ -77,6 +77,14 @@ std::string GetInputDeviceName(int dev_type)
str = "Arcade joystick";
break;
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
str = "Passthrough steel battalion controller";
break;
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
str = "Passthrough original xbox gamepad";
break;
case to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID):
str = "None";
break;
@ -109,7 +117,7 @@ std::string PortUserFormat(std::string_view port)
void PortStr2Int(std::string_view port, int *port_num, int *slot)
{
*slot = PORT_INVALID;
auto &ret = std::from_chars(port.data(), port.data() + port.size(), *port_num);
auto ret = std::from_chars(port.data(), port.data() + port.size(), *port_num);
assert(ret.ec != std::errc::invalid_argument);
if (ret.ptr != port.data() + port.size()) {
++ret.ptr;
@ -170,7 +178,7 @@ const auto InputDevice::FindPort(std::string_view Port) const
});
}
void InputDevice::SetPort(std::string_view Port, bool Connect)
void InputDevice::SetPort2(std::string_view Port, bool Connect)
{
if (Connect) {
m_XboxPort.emplace_back(Port);

View file

@ -49,13 +49,16 @@ typedef enum class _XBOX_INPUT_DEVICE : int {
DEVICE_INVALID = -1,
MS_CONTROLLER_DUKE,
MS_CONTROLLER_S,
LIGHT_GUN,
LIGHTGUN,
STEERING_WHEEL,
MEMORY_UNIT,
IR_DONGLE,
STEEL_BATTALION_CONTROLLER,
ARCADE_STICK,
DEVICE_MAX,
// Devices with the HW_ prefix (= hardware) indicate a real xbox device. Always add these after DEVICE_MAX
HW_STEEL_BATTALION_CONTROLLER,
HW_XBOX_CONTROLLER,
}
XBOX_INPUT_DEVICE;
@ -119,7 +122,10 @@ public:
// retrieves the port this device is attached to
bool GetPort(std::string_view Port) const;
// sets the port this device is attached to
void SetPort(std::string_view Port, bool Connect);
// NOTE: using SetPort2 to avoid a collision with the SetPort macro provided by Windows headers
void SetPort2(std::string_view Port, bool Connect);
// retuns true if it is a libusb device, false otherwise
virtual bool IsLibusb() const { return false; };
protected:

View file

@ -35,28 +35,35 @@
#define _XBOXKRNL_DEFEXTRN_
#define LOG_PREFIX CXBXR_MODULE::INPSYS
#include <core\kernel\exports\xboxkrnl.h> // For PKINTERRUPT, etc.
#include "common/cxbxr.hpp"
#include "SdlJoystick.h"
#include "XInputPad.h"
#include "RawDevice.h"
#include "DInputKeyboardMouse.h"
#include "LibusbDevice.h"
#include "InputManager.h"
#include "..\devices\usb\XidGamepad.h"
#include "core\kernel\exports\EmuKrnl.h" // For EmuLog
#include "EmuShared.h"
#include "devices\usb\OHCI.h"
#ifdef CXBXR_EMU
#include "core/common/video/RenderBase.hpp"
#endif
#include <charconv>
// hle input specific
#include "core\hle\XAPI\Xapi.h"
// Allocate enough memory for the max number of devices we can support simultaneously
// 4 duke / S / sbc / arcade joystick / lightgun (mutually exclusive) + 8 memory units
DeviceState g_devs[MAX_DEVS];
int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = {
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_DUKE
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_S
0,
LIGHTGUN_NUM_BUTTONS, // LIGHTGUN
0,
0,
0,
@ -77,26 +84,30 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
m_hwnd = hwnd;
m_PollingThread = std::thread([this, is_gui]() {
#ifdef CXBXR_EMU
// This code can run in both cxbx.exe and cxbxr-ldr.exe, but will not have
// the affinity policy when running in the former.
if (g_AffinityPolicy) {
g_AffinityPolicy->SetAffinityOther();
}
#endif
XInput::Init(m_Mtx);
RawInput::Init(m_Mtx, is_gui, m_hwnd);
Libusb::Init(m_Mtx);
Sdl::Init(m_Mtx, m_Cv, is_gui);
});
m_Cv.wait(lck, []() {
return (Sdl::InitStatus != Sdl::NOT_INIT) &&
(XInput::InitStatus != XInput::NOT_INIT) &&
(RawInput::InitStatus != RawInput::NOT_INIT);
(RawInput::InitStatus != RawInput::NOT_INIT) &&
(Libusb::InitStatus != Libusb::NOT_INIT);
});
lck.unlock();
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0) {
CxbxKrnlCleanup("Failed to initialize input subsystem! Consult debug log for more information");
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0 || Libusb::InitStatus < 0) {
CxbxrAbort("Failed to initialize input subsystem! Consult debug log for more information");
}
UpdateOpt(is_gui);
@ -129,8 +140,11 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
}
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
ConstructHleInputDevice(&g_devs[CTRL_OFFSET + i], nullptr, type, port);
BindHostDevice(type, port);
break;
@ -162,6 +176,7 @@ void InputDeviceManager::Shutdown()
XInput::DeInit();
RawInput::DeInit();
Libusb::DeInit();
Sdl::DeInit(m_PollingThread);
}
@ -266,7 +281,7 @@ void InputDeviceManager::UpdateDevices(std::string_view port, bool ack)
else {
auto host_dev = g_InputDeviceManager.FindDevice(port);
if (host_dev != nullptr) {
host_dev->SetPort(port, false);
host_dev->SetPort2(port, false);
}
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
if (type != to_underlying(dev->type)) {
@ -303,7 +318,7 @@ void InputDeviceManager::DisconnectDevice(DeviceState *dev, std::string_view por
}
auto host_dev = g_InputDeviceManager.FindDevice(port);
if (host_dev != nullptr) {
host_dev->SetPort(port, false);
host_dev->SetPort2(port, false);
}
}
@ -313,6 +328,19 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
// MUs don't have any host devices bound, so we just return
return;
}
else if ((type == to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER)) ||
(type == to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER))) {
// libusb devices don't have any profiles, but we still need to attach them to the xbox port
char dev_name[50];
int port_num, slot;
PortStr2Int(port, &port_num, &slot);
g_EmuShared->GetInputDevNameSettings(dev_name, port_num);
auto dev = FindDevice(std::string(dev_name));
if (dev != nullptr) {
dev->SetPort2(port, true);
}
return;
}
char dev_name[50];
char dev_control_names[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
@ -336,20 +364,22 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
});
dev->SetBindings(index, (it != controls.end()) ? *it : nullptr, port_str);
}
dev->SetPort(port, true);
dev->SetPort2(port, true);
}
}
bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int direction, int type)
{
assert(direction == DIRECTION_IN || direction == DIRECTION_OUT);
assert(type > to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX));
bool has_changed = false;
// First check if ImGui is focus, then ignore any input update occur.
// If somebody else is currently holding the lock, we won't wait and instead report no input changes
if (!g_renderbase->IsImGuiFocus() && m_Mtx.try_lock()) {
if (
#ifdef CXBXR_EMU
!g_renderbase->IsImGuiFocus() &&
#endif
m_Mtx.try_lock()) {
for (auto &dev : m_Devices) {
std::string port_str = std::to_string(port);
if (dev->GetPort(port_str)) {
@ -362,6 +392,11 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
has_changed = UpdateInputLightgun(dev, buffer, direction, port, port_str);
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
has_changed = UpdateInputSBC(dev, buffer, direction, port, port_str);
m_Mtx.unlock();
@ -371,7 +406,12 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
assert(0);
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
has_changed = UpdateInputHw(dev, buffer, direction);
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
EmuLog(LOG_LEVEL::ERROR2, "An unsupported device is attached at port %d! The device was %s",
@ -397,8 +437,7 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
return false;
}
//XpadInput* in_buf = reinterpret_cast<XpadInput*>(static_cast<uint8_t*>(Buffer) + 2); lle usb
XpadInput *in_buf = static_cast<XpadInput *>(Buffer);
XpadInput* in_buf = reinterpret_cast<XpadInput*>(static_cast<uint8_t*>(Buffer) + XID_PACKET_HEADER);
for (int i = 0; i < 8; i++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input*>(bindings[i])->GetState() : 0.0;
if (state) {
@ -441,8 +480,7 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
}
else {
if (bindings[24] != nullptr) {
//XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(static_cast<uint8_t*>(Buffer) + 2); lle usb
XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(Buffer);
XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(static_cast<uint8_t*>(Buffer) + XID_PACKET_HEADER);
dynamic_cast<InputDevice::Output*>(bindings[24])->SetState(out_buf->left_actuator_strength / static_cast<ControlState>(0xFFFF),
out_buf->right_actuator_strength / static_cast<ControlState>(0xFFFF));
}
@ -451,6 +489,130 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
return true;
}
bool InputDeviceManager::UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port)
{
std::map<int, InputDevice::IoControl *> bindings = Device->GetBindings(Port);
assert(bindings.size() == static_cast<size_t>(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN)]));
// NOTE: the output state is not supported
if (Direction == DIRECTION_IN) {
if (!Device->UpdateInput()) {
return false;
}
// We change the toggle buttons only when a press -> release input transaction is completed
// 0 -> Turbo left
// 1 -> Turbo right
// 2 -> Laser
XpadInput *in_buf = reinterpret_cast<XpadInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
g_devs[Port_num].info.ligthgun.last_turbo = g_devs[Port_num].info.ligthgun.turbo;
for (int i = 14, j = 0; i < 17; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint8_t curr_state = static_cast<uint8_t>(!!state);
if ((~curr_state) & ((g_devs[Port_num].info.ligthgun.last_in_state >> j) & 1)) {
switch (j)
{
case 0:
if (g_devs[Port_num].info.ligthgun.turbo != 2) {
g_devs[Port_num].info.ligthgun.turbo += 1;
}
break;
case 1:
if (g_devs[Port_num].info.ligthgun.turbo != 0) {
g_devs[Port_num].info.ligthgun.turbo -= 1;
}
break;
case 2:
g_devs[Port_num].info.ligthgun.laser ^= 1;
break;
}
}
(g_devs[Port_num].info.ligthgun.last_in_state &= ~(1 << j)) |= (curr_state << j);
}
in_buf->wButtons = XINPUT_LIGHTGUN_ONSCREEN;
for (int i = 0; i < 6; i++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
if (state) {
in_buf->wButtons |= (1 << i);
}
else {
in_buf->wButtons &= ~(1 << i);
}
}
if (g_devs[Port_num].info.ligthgun.turbo) {
ControlState trigger_state = (bindings[6] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[6])->GetState() : 0.0;
if (trigger_state) {
// Turbo mode 1
in_buf->bAnalogButtons[0] ^= 0xFF;
int start_idx = 7;
if (g_devs[Port_num].info.ligthgun.turbo == 2) {
// Turbo mode 2
start_idx = 8;
++g_devs[Port_num].info.ligthgun.turbo_delay;
if (g_devs[Port_num].info.ligthgun.last_turbo != g_devs[Port_num].info.ligthgun.turbo) {
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
}
if (g_devs[Port_num].info.ligthgun.turbo_delay == LIGHTGUN_GRIP_DELAY) {
in_buf->bAnalogButtons[1] ^= 0xFF;
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
}
}
for (int i = start_idx, j = start_idx - 6; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
else {
// Turbo active but trigger not pressed
in_buf->bAnalogButtons[0] = 0;
for (int i = 7, j = 1; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
}
else {
// Turbo mode 0 (no turbo)
for (int i = 6, j = 0; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
in_buf->bAnalogButtons[4] = 0;
in_buf->bAnalogButtons[5] = 0;
in_buf->bAnalogButtons[6] = 0;
in_buf->bAnalogButtons[7] = 0;
for (int i = 10; i < 14; i += 2) {
ControlState state_plus = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
ControlState state_minus = (bindings[i + 1] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i + 1])->GetState() : 0.0;
ControlState state = state_plus ? state_plus * 0x7FFF : state_minus ? -state_minus * 0x8000 : 0.0;
switch (i)
{
case 10: {
xbox::short_xt offset = std::abs(state) > 16383.0 ? g_devs[Port_num].info.ligthgun.offset_upp_x : g_devs[Port_num].info.ligthgun.offset_x;
in_buf->sThumbLX = static_cast<int16_t>(state) + offset;
}
break;
case 12: {
xbox::short_xt offset = std::abs(state) > 16383.0 ? g_devs[Port_num].info.ligthgun.offset_upp_y : g_devs[Port_num].info.ligthgun.offset_y;
in_buf->sThumbLY = static_cast<int16_t>(state) + offset;
}
break;
}
}
in_buf->sThumbRX = in_buf->sThumbRY = 0;
}
return true;
}
bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port)
{
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port);
@ -474,8 +636,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
// 8 -> TunerDial Down
// 9 -> GearLever Up
// 10 -> GearLever Down
static uint16_t last_in_state[XBOX_NUM_PORTS] = { 0, 0, 0, 0 };
SBCInput *in_buf = static_cast<SBCInput *>(Buffer);
SBCInput *in_buf = reinterpret_cast<SBCInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
for (int i = 0; i < 4; i++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
if (state) {
@ -490,10 +651,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 4, j = 0; i < 6; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
in_buf->wButtons[0] ^= (1 << i);
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
for (int i = 6; i < 34; i++) {
@ -510,10 +671,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 34, j = 2; i < 39; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
in_buf->wButtons[2] ^= (1 << (i % 16));
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
for (int i = 39; i < 49; i += 2) {
@ -572,7 +733,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 52, j = 7; i < 56; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
switch (i)
{
case 52:
@ -600,13 +761,18 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
break;
}
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
}
return true;
}
bool InputDeviceManager::UpdateInputHw(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction)
{
return dynamic_cast<Libusb::LibusbDevice *>(Device.get())->ExecuteIo(Buffer, Direction);
}
void InputDeviceManager::RefreshDevices()
{
std::unique_lock<std::mutex> lck(m_Mtx);
@ -616,25 +782,28 @@ void InputDeviceManager::RefreshDevices()
XInput::PopulateDevices();
DInput::PopulateDevices();
Sdl::PopulateDevices();
Libusb::PopulateDevices();
lck.lock();
m_Cv.wait(lck, []() {
return Sdl::PopulateOK;
});
for (auto &dev : m_Devices) {
if (StrStartsWith(dev->GetDeviceName(), "KeyboardMouse")) {
if (dev->GetDeviceName().starts_with("KeyboardMouse")) {
static_cast<DInput::KeyboardMouse *>(dev.get())->SetHwnd(m_hwnd);
break;
}
}
}
std::vector<std::string> InputDeviceManager::GetDeviceList() const
std::vector<std::string> InputDeviceManager::GetDeviceList(std::function<bool(const InputDevice *)> Callback) const
{
std::vector<std::string> dev_list;
std::lock_guard<std::mutex> lck(m_Mtx);
std::for_each(m_Devices.begin(), m_Devices.end(), [&dev_list](const auto& Device) {
std::for_each(m_Devices.begin(), m_Devices.end(), [&dev_list, &Callback](const auto& Device) {
if (Callback(Device.get())) {
dev_list.push_back(Device->GetQualifiedName());
}
});
return dev_list;
@ -719,7 +888,7 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
std::unique_lock<std::mutex> lck(m_Mtx);
auto it = std::remove_if(m_Devices.begin(), m_Devices.end(), [](const auto &Device) {
if (StrStartsWith(Device->GetAPI(), "XInput")) {
if (Device->IsLibusb() || Device->GetAPI().starts_with("XInput")) {
return true;
}
return false;
@ -730,6 +899,9 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
lck.unlock();
XInput::PopulateDevices();
// When this was written, libusb did not yet support device hotplug on Windows, as documented in this issue https://github.com/libusb/libusb/issues/86.
// So we add the below call here. This will only work if rawinput detects the libusb device.
Libusb::PopulateDevices();
}
for (int port = PORT_1; port <= PORT_4; ++port) {
@ -740,3 +912,35 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
}
}
}
ImVec2 InputDeviceManager::CalcLaserPos(int port)
{
static ImVec2 laser_coord[XBOX_NUM_PORTS] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
// If somebody else is currently holding the lock, we won't wait and instead we report the last known laser position
if (m_Mtx.try_lock()) {
// NOTE: even when switching to faux fullscreen, imgui will still use the original window size. Because of this, we only need to do this once.
// If in the future the above is fixed, then this code will have to recalculate the new window size after a resizing has occured.
static long width, height = -1;
if (height == -1) {
RECT rect;
GetClientRect(m_hwnd, &rect);
width = std::max(rect.right - rect.left, 1l);
height = std::max(rect.bottom - rect.top, 1l);
}
// We convert the laser input coordinates given by xinput (in the sThumbLXY members of XpadInput) with linear interpolation y = y0 + (x - x0) * (y1 - y0) / (x1 - x0)
// For laser_x x0 = -32768; x1 = 32767; y0 = 0; y1 = width
// For laser_y x0 = -32768; x1 = 32767; y0 = height; y1 = 0
int16_t laser_x = g_devs[port].info.buff.ctrl.InBuffer.sThumbLX;
int16_t laser_y = g_devs[port].info.buff.ctrl.InBuffer.sThumbLY;
laser_coord[port].x = ((laser_x + 32768) * width) / 65535.0f;
laser_coord[port].y = height - ((laser_y + 32768) * height) / 65535.0f;
m_Mtx.unlock();
}
return laser_coord[port];
}

View file

@ -31,11 +31,7 @@
#include <thread>
#include "InputDevice.h"
#include "EmuDevice.h"
// Prevent a collision with the SetPort provided by Windows
#ifdef WIN32
#undef SetPort
#endif
#include <imgui.h>
#define PORT_INVALID -1
#define PORT_1 0
@ -52,14 +48,21 @@
#define MU_OFFSET 4
#define MAX_DEVS (XBOX_NUM_PORTS + XBOX_CTRL_NUM_SLOTS * XBOX_NUM_PORTS)
#define XID_PACKET_HEADER 2
#define LIGHTGUN_GRIP_DELAY 30
extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)];
inline XBOX_INPUT_DEVICE input_support_list[] = {
XBOX_INPUT_DEVICE::DEVICE_INVALID,
XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE,
XBOX_INPUT_DEVICE::MS_CONTROLLER_S,
XBOX_INPUT_DEVICE::LIGHTGUN,
XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER,
XBOX_INPUT_DEVICE::ARCADE_STICK,
XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER,
XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER,
};
inline XBOX_INPUT_DEVICE slot_support_list[] = {
@ -71,7 +74,19 @@ inline XBOX_INPUT_DEVICE slot_support_list[] = {
#pragma pack(1)
// xpad in/out buffers stripped of the first two bytes
// Class-specific xid descriptor, used by libusb
struct XidDesc {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdXid;
uint8_t bType;
uint8_t bSubType;
uint8_t bMaxInputReportSize;
uint8_t bMaxOutputReportSize;
uint16_t wAlternateProductIds[4];
};
// xpad in/out buffers stripped of the first two bytes as used by xinput
struct XpadInput {
uint16_t wButtons;
uint8_t bAnalogButtons[8];
@ -86,6 +101,20 @@ struct XpadOutput {
uint16_t right_actuator_strength;
};
// xpad in/out buffers as used by xid
struct XidGamepadInput {
uint8_t bReportId;
uint8_t bLength;
XpadInput InBuffer;
};
struct XidGamepadOutput {
uint8_t bReportId;
uint8_t bLength;
XpadOutput OutBuffer;
};
// same as above, but for the SBC
struct SBCInput {
uint16_t wButtons[3];
uint8_t bPad1;
@ -112,11 +141,39 @@ struct SBCOutput {
uint8_t LedState[20];
};
struct XidSBCInput {
uint8_t bReportId;
uint8_t bLength;
SBCInput InBuffer;
};
struct XidSBCOutput {
uint8_t bReportId;
uint8_t bLength;
SBCOutput OutBuffer;
};
#pragma pack()
struct LightGunData {
xbox::short_xt offset_x;
xbox::short_xt offset_y;
xbox::short_xt offset_upp_x;
xbox::short_xt offset_upp_y;
uint8_t last_in_state;
uint8_t last_turbo;
uint8_t turbo_delay;
uint8_t turbo;
uint8_t laser;
};
struct SbcData {
uint16_t last_in_state;
};
union InputBuff {
XpadInput ctrl;
SBCInput sbc;
XidGamepadInput ctrl;
XidSBCInput sbc;
};
struct DeviceInfo {
@ -127,8 +184,13 @@ struct DeviceInfo {
uint8_t ucSubType; // xapi subtype
uint8_t ucInputStateSize; // input state size in bytes, does not include dwPacketNumber
uint8_t ucFeedbackSize; // feedback size in bytes, does not include FeedbackHeader
uint32_t dwPacketNumber;
InputBuff buff;
uint32_t dwPacketNumber; // increases by one when the input state changes
InputBuff buff; // input buffer
// device-specific additional data
union {
LightGunData ligthgun;
SbcData sbc;
};
};
struct DeviceState {
@ -159,7 +221,7 @@ public:
// update device list
void RefreshDevices();
// get the name of the devices currently detected
std::vector<std::string> GetDeviceList() const;
std::vector<std::string> GetDeviceList(std::function<bool(const InputDevice *)> Callback) const;
// find device from its gui name
std::shared_ptr<InputDevice> FindDevice(const std::string& QualifiedName) const;
// find device from its sdl id
@ -172,13 +234,19 @@ public:
void UpdateOpt(bool is_gui);
// device hotplug event handler
void HotplugHandler(bool is_sdl);
// converts xinput -> screen coordinates to display the lightgun laser on the rendering window
ImVec2 CalcLaserPos(int port);
private:
// update input for an xbox controller
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port);
// update input for a lightgun
bool UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port);
// update input for a Steel Battalion controller
bool UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port);
// update input for a passthrough xbox device
bool UpdateInputHw(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction);
// bind a host device to an emulated device
void BindHostDevice(int type, std::string_view port);
// connect a device to the emulated machine

View file

@ -78,16 +78,22 @@ void InputWindow::UpdateDeviceList()
g_InputDeviceManager.RefreshDevices();
// Populate device list
LRESULT num_devices = SendMessage(m_hwnd_device_list, CB_GETCOUNT, 0, 0);
for (int i = 0; i < num_devices; i++) {
for (int i = 0; i < m_num_devices; i++) {
SendMessage(m_hwnd_device_list, CB_DELETESTRING, 0, 0);
}
std::vector<std::string> dev_list = g_InputDeviceManager.GetDeviceList();
// Add everything but libusb devices
std::vector<std::string> dev_list = g_InputDeviceManager.GetDeviceList([](const auto &Device) {
if (Device->IsLibusb()) {
return false;
}
return true;
});
for (const auto& str : dev_list) {
SendMessage(m_hwnd_device_list, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(str.c_str()));
++m_num_devices;
}
if (!dev_list.empty()) {
if (m_num_devices) {
SendMessage(m_hwnd_device_list, CB_SETCURSEL, 0, 0);
}
@ -144,7 +150,23 @@ InputDevice::Input* InputWindow::DetectInput(InputDevice* const Device, int ms)
return nullptr; // no input
}
void InputWindow::BindButton(int ControlID)
int InputWindow::EnableDefaultButton()
{
if (std::strncmp(m_host_dev.c_str(), "XInput", std::strlen("XInput")) == 0) {
EnableWindow(m_hwnd_default, TRUE);
return XINPUT_DEFAULT;
}
else if (std::strncmp(m_host_dev.c_str(), "DInput", std::strlen("DInput")) == 0) {
EnableWindow(m_hwnd_default, TRUE);
return DINPUT_DEFAULT;
}
else {
EnableWindow(m_hwnd_default, FALSE);
return -1;
}
}
void InputWindow::BindButton(int ControlID, bool auto_swap)
{
// Check if binding thread is still active
// testcase: spacebar and enter keys; without this fix will cause repeat binding result.
@ -157,7 +179,7 @@ void InputWindow::BindButton(int ControlID)
m_bIsBinding = true;
// Don't block the message processing loop
std::thread([this, dev, ControlID]() {
std::thread([this, dev, ControlID, auto_swap]() {
EnableWindow(m_hwnd_window, FALSE);
char current_text[HOST_BUTTON_NAME_LENGTH];
Button* xbox_button = m_DeviceConfig->FindButtonById(ControlID);
@ -167,6 +189,9 @@ void InputWindow::BindButton(int ControlID)
InputDevice::Input* dev_button = fut.get();
if (dev_button) {
xbox_button->UpdateText(dev_button->GetName().c_str());
if (auto_swap) {
SwapMoCursorAxis(xbox_button);
}
m_bHasChanges = true;
}
else {
@ -195,7 +220,6 @@ void InputWindow::UpdateProfile(const std::string &name, int command)
break;
case BUTTON_CLEAR:
case BUTTON_SWAP:
m_bHasChanges = true;
break;
}
@ -310,9 +334,15 @@ void InputWindow::LoadDefaultProfile()
void InputWindow::UpdateCurrentDevice()
{
if (m_num_devices) {
char device_name[50];
SendMessage(m_hwnd_device_list, WM_GETTEXT, sizeof(device_name), reinterpret_cast<LPARAM>(device_name));
m_host_dev = device_name;
}
else {
m_host_dev = "";
}
EnableDefaultButton();
}
@ -322,11 +352,11 @@ void InputWindow::SwapMoCursorAxis(Button *button)
// Axis X- <-> Cursor X-
// Axis Y+ <-> Cursor Y-
// Axis Y- <-> Cursor Y+
if (StrEndsWith(m_host_dev, "KeyboardMouse")) {
if (m_host_dev.ends_with("KeyboardMouse")) {
assert(button != nullptr);
char control_name[HOST_BUTTON_NAME_LENGTH];
button->GetText(control_name, sizeof(control_name));
if (StrStartsWith(control_name, "Axis")) {
if (std::string_view(control_name).starts_with("Axis")) {
switch (control_name[5])
{
case 'X':
@ -336,6 +366,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
else {
button->UpdateText("Cursor X-");
}
m_bHasChanges = true;
break;
case 'Y':
@ -345,6 +376,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
else {
button->UpdateText("Cursor Y+");
}
m_bHasChanges = true;
break;
}
@ -352,7 +384,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
return;
}
if (StrStartsWith(control_name, "Cursor")) {
if (std::string_view(control_name).starts_with("Cursor")) {
switch (control_name[7])
{
case 'X':
@ -362,6 +394,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
else {
button->UpdateText("Axis X-");
}
m_bHasChanges = true;
break;
case 'Y':
@ -371,6 +404,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
else {
button->UpdateText("Axis Y+");
}
m_bHasChanges = true;
break;
}

View file

@ -39,7 +39,6 @@
#define RUMBLE_TEST 6
#define RUMBLE_CLEAR 7
#define BUTTON_CLEAR 8
#define BUTTON_SWAP 9
#define XINPUT_DEFAULT 0
#define DINPUT_DEFAULT 1
@ -55,8 +54,8 @@ class InputWindow
public:
virtual void Initialize(HWND hwnd, int port_num, int dev_type) = 0;
~InputWindow();
void UpdateDeviceList();
void BindButton(int ControlID);
virtual void UpdateDeviceList();
void BindButton(int ControlID, bool auto_swap = false);
virtual void ClearBindings() = 0;
virtual void UpdateProfile(const std::string& name, int command);
void UpdateCurrentDevice();
@ -73,8 +72,8 @@ protected:
void DeleteProfile(const std::string& name);
void OverwriteProfile(const std::string& name);
void LoadDefaultProfile();
virtual int EnableDefaultButton() = 0;
virtual void SaveSlotConfig() = 0;
virtual int EnableDefaultButton();
// xbox device under configuration
EmuDevice* m_DeviceConfig;
@ -84,6 +83,10 @@ protected:
HWND m_hwnd_device_list;
// handle of the profile list combobox
HWND m_hwnd_profile_list;
// handle of the default bindings button
HWND m_hwnd_default;
// number of devices displayed in the device list combobox
int m_num_devices;
// type of the device
int m_dev_type;
// num of buttons of device under configuration
@ -107,15 +110,12 @@ public:
void BindDefault();
void ClearBindings() override;
void UpdateProfile(const std::string &name, int command) override;
void SaveSlotConfig() override;
void SaveSlotConfig();
private:
int EnableDefaultButton() override;
void DetectOutput(int ms);
// handle of the default bindings button
HWND m_hwnd_default;
// handle of the rumble window
HWND m_hwnd_rumble;
// handle of the rumble combobox
@ -130,10 +130,36 @@ class SbcInputWindow : public InputWindow
{
public:
void Initialize(HWND hwnd, int port_num, int dev_type) override;
~SbcInputWindow();
void ClearBindings() override;
void SaveSlotConfig() override;
private:
int EnableDefaultButton() override;
};
class LibusbInputWindow : public InputWindow
{
public:
void Initialize(HWND hwnd, int port_num, int dev_type) override;
~LibusbInputWindow();
void ClearBindings() override;
void UpdateDeviceList() override;
void TestInput();
private:
int EnableDefaultButton() override;
// handle of the test button
HWND m_hwnd_device_test;
};
class LightgunInputWindow : public InputWindow
{
public:
void Initialize(HWND hwnd, int port_num, int dev_type) override;
~LightgunInputWindow();
void BindDefault();
void ClearBindings() override;
};

View file

@ -0,0 +1,325 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::LIBUSB
#include "LibusbDevice.h"
#include "InputManager.h"
#include "core\kernel\support\Emu.h"
#include "core\hle\XAPI\Xapi.h"
// Sanity check: ensure that our libusb version is high enough for libusb_get_device_descriptor to succeed and to pass nullptr to libusb_interrupt_transfer
static_assert(LIBUSB_API_VERSION >= 0x01000105);
namespace Libusb
{
int InitStatus = NOT_INIT;
// These come from here https://github.com/xboxdrv/xboxdrv/blob/ac6ebb1228962220482ea03743cadbe18754246c/src/xpad_device.cpp#L29
static constexpr uint16_t SupportedDevices_VidPid[][2] = { // vid, pid
0x0d2f, 0x0002,
0x045e, 0x0202,
0x045e, 0x0285,
0x045e, 0x0287,
0x045e, 0x0289,
0x046d, 0xca84,
0x046d, 0xca88,
0x05fd, 0x1007,
0x05fd, 0x107a,
0x0738, 0x4516,
0x0738, 0x4522,
0x0738, 0x4526,
0x0738, 0x4536,
0x0738, 0x4556,
0x0c12, 0x8802,
0x0c12, 0x8810,
0x0c12, 0x9902,
0x0e4c, 0x1097,
0x0e4c, 0x2390,
0x0e6f, 0x0003,
0x0e6f, 0x0005,
0x0e6f, 0x0006,
0x0f30, 0x0202,
0x0f30, 0x8888,
0x102c, 0xff0c,
0x044f, 0x0f07,
0x0e8f, 0x3008,
};
static constexpr const char *SupportedDevices_Name[] = {
"Andamiro Pump It Up pad",
"Microsoft X-Box pad v1 (US)",
"Microsoft X-Box pad (Japan)",
"Microsoft Xbox Controller S",
"Microsoft X-Box pad v2 (US)",
"Logitech Xbox Cordless Controller",
"Logitech Compact Controller for Xbox",
"Mad Catz Controller (unverified)",
"InterAct 'PowerPad Pro' X-Box pad (Germany)",
"Mad Catz Control Pad",
"Mad Catz LumiCON",
"Mad Catz Control Pad Pro",
"Mad Catz MicroCON",
"Mad Catz Lynx Wireless Controller",
"Zeroplus Xbox Controller",
"Zeroplus Xbox Controller",
"HAMA VibraX - *FAULTY HARDWARE*",
"Radica Gamester Controller",
"Radica Games Jtech Controller",
"Logic3 Freebird wireless Controller",
"Eclipse wireless Controller",
"Edge wireless Controller",
"Joytech Advanced Controller",
"BigBen XBMiniPad Controller",
"Joytech Wireless Advanced Controller",
"Thrustmaster, Inc. Controller",
"Generic xbox control (dealextreme)",
};
static_assert(ARRAY_SIZE(SupportedDevices_VidPid) == ARRAY_SIZE(SupportedDevices_Name));
void Init(std::mutex &Mtx)
{
std::unique_lock<std::mutex> lck(Mtx);
// We only use a single libusb session per cxbxr process, so we do not need to use a libusb context
if (libusb_init(nullptr) != 0) {
EmuLog(LOG_LEVEL::ERROR2, "Failed to initialize Libusb!");
InitStatus = INIT_ERROR;
return;
}
InitStatus = INIT_SUCCESS;
}
void DeInit()
{
InitStatus = NOT_INIT;
libusb_exit(nullptr);
}
void PopulateDevices()
{
// NOTE: the libusb docs say that the list is always appended with a NULL element at the end
libusb_device **List;
ssize_t DevicesConnected = libusb_get_device_list(nullptr, &List) - 1;
if (DevicesConnected < 0) {
EmuLog(LOG_LEVEL::ERROR2, "Failed to enumerate devices. The error was: %s", libusb_strerror(DevicesConnected));
return;
}
for (ssize_t i = 0; i < DevicesConnected; ++i) {
libusb_device *LibusbDev = List[i];
libusb_device_descriptor Desc;
libusb_get_device_descriptor(LibusbDev, &Desc); // always succeeds when LIBUSB_API_VERSION >= 0x01000102
auto Device = std::make_shared<LibusbDevice>(&Desc, LibusbDev);
if (Device->IsLibusb()) {
g_InputDeviceManager.AddDevice(std::move(Device));
}
}
libusb_free_device_list(List, 1);
}
void GetDeviceChanges()
{
g_InputDeviceManager.RemoveDevice([](const auto &Device) {
return Device->IsLibusb();
});
PopulateDevices();
}
LibusbDevice::LibusbDevice(libusb_device_descriptor *Desc, libusb_device *Dev)
{
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
// The SBC's VID and PID are taken from https://xboxdevwiki.net/Xbox_Input_Devices#Steel_Battalion_Controller
if ((Desc->idVendor == 0x0a7b) && (Desc->idProduct == 0xd000)) {
m_Type = XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER;
m_UcType = XINPUT_DEVTYPE_STEELBATTALION;
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
m_Name = "Steel battalion controller";
m_BufferInSize = sizeof(XidSBCInput);
m_BufferOutSize = sizeof(XidSBCOutput);
assert(Desc->bcdUSB == 0x110); // must be a usb 1.1 device
}
else {
for (size_t i = 0; i < ARRAY_SIZE(SupportedDevices_VidPid); ++i) {
if ((Desc->idVendor == SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
m_Type = XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER;
m_UcType = XINPUT_DEVTYPE_GAMEPAD;
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
m_Name = SupportedDevices_Name[i];
m_BufferInSize = sizeof(XidGamepadInput);
m_BufferOutSize = sizeof(XidGamepadOutput);
assert(Desc->bcdUSB == 0x110); // must be a usb 1.1 device
break;
}
}
}
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
// check if we are able to open device through libusb
if (int err = libusb_open(Dev, &m_hDev)) {
// Couldn't open device, create an error log report then don't use it.
EmuLog(LOG_LEVEL::ERROR2, "Unable to open original xbox device \"%s\" (%hX:%hX), libusb's error was: %s",
m_Name.c_str(), Desc->idVendor, Desc->idProduct, libusb_strerror(err));
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
return;
}
// If we are able to open device, continue with query process.
else {
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
// The code below assumes that third-party controllers follow suit.
libusb_config_descriptor *ConfDesc;
if (libusb_get_active_config_descriptor(Dev, &ConfDesc) == 0) {
if (ConfDesc->bNumInterfaces == 1) {
auto Iface = ConfDesc->interface[0];
if (Iface.num_altsetting == 1) {
auto Setting = Iface.altsetting[0];
m_IfaceNum = Setting.bInterfaceNumber;
if (Setting.bNumEndpoints >= 1) {
m_HasEndpointOut = false;
for (uint8_t i = 0; i < Setting.bNumEndpoints; ++i) {
auto Endpoint = Setting.endpoint[i];
if (Endpoint.bEndpointAddress & 0x80) {
m_EndpointIn = Endpoint.bEndpointAddress;
m_IntervalIn = Endpoint.bInterval;
}
else {
// third-party controllers that don't support rumble probably won't have an out endpoint
m_EndpointOut = Endpoint.bEndpointAddress;
m_IntervalOut = Endpoint.bInterval;
m_HasEndpointOut = true;
}
}
EmuLog(LOG_LEVEL::INFO, "Out endpoint %s", m_HasEndpointOut ? "present" : "not present");
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum)) {
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because libusb could not claim its interface. The error was: %s",
m_Name.c_str(), libusb_strerror(err));
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
}
else {
// Grab the xid descriptor so that we can report real type/subtype values back to the title when it calls XInputGetCapabilities
XidDesc XidDesc;
if (libusb_control_transfer(m_hDev, 0xC1, 6, 0x4200, m_IfaceNum, reinterpret_cast<uint8_t *>(&XidDesc), sizeof(XidDesc), m_IntervalIn)
== sizeof(XidDesc)) { // submit a GET_DESCRIPTOR request
// Dump the xid descriptor to the log
EmuLog(LOG_LEVEL::INFO, "Xid descriptor dump:\nbLength: %#010X\nbDescriptorType: %#010X\nbcdXid: %#010X\nbType: %#010X\n"
"bSubType: %#010X\nbMaxInputReportSize: %#010X\nbMaxOutputReportSize: %#010X\nwAlternateProductIds[0]: %#010X\n"
"wAlternateProductIds[1]: %#010X\nwAlternateProductIds[2]: %#010X\nwAlternateProductIds[3]: %#010X\n",
XidDesc.bLength, XidDesc.bDescriptorType, XidDesc.bcdXid, XidDesc.bType, XidDesc.bSubType, XidDesc.bMaxInputReportSize, XidDesc.bMaxOutputReportSize,
XidDesc.wAlternateProductIds[0], XidDesc.wAlternateProductIds[1], XidDesc.wAlternateProductIds[2], XidDesc.wAlternateProductIds[3]);
if (XidDesc.bDescriptorType == 0x42) {
m_UcType = XidDesc.bType;
m_UcSubType = XidDesc.bSubType;
}
else {
EmuLog(LOG_LEVEL::INFO, "The xid descriptor for device %s reported an unexpected descriptor type, assuming a default subtype", m_Name);
}
}
else {
EmuLog(LOG_LEVEL::INFO, "Could not retrieve the xid descriptor for device %s, assuming a default subtype", m_Name);
}
}
}
else {
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of endpoints, bNumEndpoints: %d",
m_Name.c_str(), Setting.bNumEndpoints);
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
}
}
else {
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of alternative settings, num_altsetting: %d",
m_Name.c_str(), Iface.num_altsetting);
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
}
}
else {
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of interfaces, bNumInterfaces: %d",
m_Name.c_str(), ConfDesc->bNumInterfaces);
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
}
libusb_free_config_descriptor(ConfDesc);
}
}
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { libusb_close(m_hDev); }
}
LibusbDevice::~LibusbDevice()
{
if (m_Type != XBOX_INPUT_DEVICE::DEVICE_INVALID) {
libusb_release_interface(m_hDev, m_IfaceNum);
libusb_close(m_hDev);
}
}
bool LibusbDevice::UpdateInput()
{
// Dummy, it should never be called. It's only here to override the pure function UpdateInput in InputDevice
assert(0);
return false;
}
bool LibusbDevice::ExecuteIo(void *Buffer, int Direction)
{
// NOTE: a SET_REPORT control transfer to the SBC doesn't seem to work, the parameters might not be appropriate for it... So, we use
// the interrupt pipes for everything instead
*static_cast<uint8_t *>(Buffer) = 0; // write bReportId
if (Direction == DIRECTION_IN) {
*(static_cast<uint8_t *>(Buffer) + 1) = m_BufferInSize; // write bLength
if (libusb_interrupt_transfer(m_hDev, m_EndpointIn, static_cast<uint8_t *>(Buffer), m_BufferInSize, nullptr, m_IntervalIn) != 0) {
return false;
}
}
else {
if (m_HasEndpointOut) {
*(static_cast<uint8_t *>(Buffer) + 1) = m_BufferOutSize; // write bLength
if (libusb_interrupt_transfer(m_hDev, m_EndpointOut, static_cast<uint8_t *>(Buffer), m_BufferOutSize, nullptr, m_IntervalOut) != 0) {
return false;
}
}
}
return true;
}
std::string LibusbDevice::GetDeviceName() const
{
return m_Name;
}
std::string LibusbDevice::GetAPI() const
{
return "Libusb";
}
}

View file

@ -0,0 +1,91 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
#include "InputDevice.h"
// Suppress warning in libusb about zero sized array
#pragma warning(push)
#pragma warning(disable: 4200)
#include "libusb.h"
#pragma warning(pop)
namespace Libusb
{
typedef enum _INIT_STATUS : int
{
NOT_INIT = -2,
INIT_ERROR,
INIT_SUCCESS,
}
INIT_STATUS;
extern int InitStatus;
// initialize Libusb
void Init(std::mutex &Mtx);
// shutdown Libusb
void DeInit();
// refresh the device list in response to a refresh command from the input GUI
void PopulateDevices();
// update the device list
void GetDeviceChanges();
class LibusbDevice : public InputDevice
{
public:
~LibusbDevice();
bool UpdateInput() override;
bool ExecuteIo(void *Buffer, int Direction);
LibusbDevice(libusb_device_descriptor *Desc, libusb_device *Dev);
std::string GetDeviceName() const override;
std::string GetAPI() const override;
bool IsLibusb() const override { return m_Type != XBOX_INPUT_DEVICE::DEVICE_INVALID; }
XBOX_INPUT_DEVICE GetLibusbType() const { return m_Type; }
uint8_t GetUcType() { return m_UcType; }
uint8_t GetUcSubType() { return m_UcSubType; }
private:
XBOX_INPUT_DEVICE m_Type;
uint8_t m_UcType;
uint8_t m_UcSubType;
std::string m_Name;
libusb_device_handle *m_hDev;
unsigned char m_EndpointIn;
unsigned char m_EndpointOut;
uint8_t m_IntervalIn;
uint8_t m_IntervalOut;
uint8_t m_BufferInSize;
uint8_t m_BufferOutSize;
uint8_t m_IfaceNum;
bool m_HasEndpointOut;
};
}

View file

@ -35,12 +35,13 @@
#define LOG_PREFIX CXBXR_MODULE::SDL
#include <assert.h>
#include <algorithm>
#include <thread>
#include "core\kernel\support\Emu.h"
#include "core\kernel\init\CxbxKrnl.h"
#include "SdlJoystick.h"
#include "XInputPad.h"
#include "DInputKeyboardMouse.h"
#include "LibusbDevice.h"
#include "InputManager.h"
// These values are those used by Dolphin!
@ -156,6 +157,7 @@ namespace Sdl
else {
XInput::GetDeviceChanges();
DInput::GetDeviceChanges();
Libusb::GetDeviceChanges();
std::string port = std::to_string(*static_cast<int *>(Event.user.data1));
int port_num, slot;
PortStr2Int(port, &port_num, &slot);

View file

@ -118,6 +118,27 @@ inline int button_sbc_id[SBC_NUM_BUTTONS] = {
IDC_GEAR_UP,
IDC_GEAR_DOWN,
};
// Must have the same button order as defined in button_lightgun_names
inline int button_lightgun_id[LIGHTGUN_NUM_BUTTONS] = {
IDC_LG_STICK_UP,
IDC_LG_STICK_DOWN,
IDC_LG_STICK_LEFT,
IDC_LG_STICK_RIGHT,
IDC_LG_START,
IDC_LG_SEBA,
IDC_LG_TRIGGER,
IDC_LG_GRIP,
IDC_LG_A,
IDC_LG_B,
IDC_LG_AIM_POSX,
IDC_LG_AIM_NEGX,
IDC_LG_AIM_POSY,
IDC_LG_AIM_NEGY,
IDC_TURBO_LEFT,
IDC_TURBO_RIGHT,
IDC_LASER,
};
#endif
inline constexpr const char* button_xbox_ctrl_names[XBOX_CTRL_NUM_BUTTONS] = {
@ -207,6 +228,26 @@ inline constexpr const char *button_sbc_names[SBC_NUM_BUTTONS] = {
"GearLever Down",
};
inline constexpr const char *button_lightgun_names[LIGHTGUN_NUM_BUTTONS] = {
"Stick Up",
"Stick Down",
"Stick Left",
"Stick Right",
"START",
"SE/BA",
"Trigger",
"Grip",
"A",
"B",
"Aim X+",
"Aim X-",
"Aim Y+",
"Aim Y-",
"Turbo Left",
"Turbo Right",
"Laser",
};
constexpr bool check_button_name_size(unsigned max_num_buttons)
{
switch (max_num_buttons)

View file

@ -52,7 +52,6 @@
#include <cstring> // For memcpy
#include "common\util\CxbxUtil.h"
#include "core\kernel\init\CxbxKrnl.h"
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
@ -280,27 +279,3 @@ std::string StripQuotes(const std::string& str)
{
return StripChars(str, "\"");
}
// NOTE: with C++20, this can be replaced by simply calling full_str.ends_with()
bool StrEndsWith(const std::string &full_str, const std::string &substr)
{
if (full_str.length() >= substr.length()) {
if (full_str.compare(full_str.length() - substr.length(), substr.length(), substr) == 0) {
return true;
}
}
return false;
}
// NOTE: with C++20, this can be replaced by simply calling full_str.starts_with()
bool StrStartsWith(const std::string &full_str, const std::string &substr)
{
if (!full_str.empty()) {
if (full_str.rfind(substr, 0) == 0) {
return true;
}
}
return false;
}

View file

@ -26,12 +26,15 @@
#ifndef CXBXUTIL_H
#define CXBXUTIL_H
#include <algorithm>
#include <stdexcept>
#include "xbox_types.h"
#include "Cxbx.h"
#include <stdint.h>
#include <assert.h>
#include <string>
#include <type_traits>
#include <vector>
#include "std_extend.hpp" // for ARRAY_SIZE
/* This is a linux struct for vectored I/O. See readv() and writev() */
@ -67,8 +70,6 @@ bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite);
void unix2dos(std::string& string);
std::string StripSpaces(const std::string& str);
std::string StripQuotes(const std::string& str);
bool StrEndsWith(const std::string &full_str, const std::string &substr);
bool StrStartsWith(const std::string &full_str, const std::string &substr);
// Retrieves the underlying integer value of a scoped enumerator. It allows to avoid using static_cast every time
template <typename E>
@ -92,4 +93,17 @@ static uint32_t RoundUp(uint32_t dwValue, uint32_t dwMult)
return dwValue + dwMult - remainder;
}
constexpr std::size_t longest_str(const std::vector<std::string_view> &vec)
{
if (!vec.empty()) {
return std::max_element(vec.begin(), vec.end(),
[](const auto &a, const auto &b) {
return a.length() < b.length();
})->length();
}
else {
throw std::logic_error("No strings to compare!");
}
}
#endif

View file

@ -1,335 +0,0 @@
/*
Copyright (c) 2013 - 2014, 2016 Mark Adler, Robert Vazan, Max Vysokikh
This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "crc32c.h"
#include <intrin.h>
#include <algorithm>
#define POLY 0x82f63b78
#define LONG_SHIFT 8192
#define SHORT_SHIFT 256
typedef const uint8_t *buffer;
static uint32_t table[16][256];
static uint32_t long_shifts[4][256];
static uint32_t short_shifts[4][256];
static bool _tableInitialized;
void calculate_table();
/* Table-driven software version as a fall-back. This is about 15 times slower
than using the hardware instructions. This assumes little-endian integers,
as is the case on Intel processors that the assembler code here is for. */
extern "C" CRC32C_API uint32_t crc32c_append_sw(uint32_t crci, buffer input, size_t length)
{
buffer next = input;
#ifdef _M_X64
uint64_t crc;
#else
uint32_t crc;
#endif
crc = crci ^ 0xffffffff;
#ifdef _M_X64
while (length && ((uintptr_t)next & 7) != 0)
{
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
--length;
}
while (length >= 16)
{
crc ^= *(uint64_t *)next;
uint64_t high = *(uint64_t *)(next + 8);
crc = table[15][crc & 0xff]
^ table[14][(crc >> 8) & 0xff]
^ table[13][(crc >> 16) & 0xff]
^ table[12][(crc >> 24) & 0xff]
^ table[11][(crc >> 32) & 0xff]
^ table[10][(crc >> 40) & 0xff]
^ table[9][(crc >> 48) & 0xff]
^ table[8][crc >> 56]
^ table[7][high & 0xff]
^ table[6][(high >> 8) & 0xff]
^ table[5][(high >> 16) & 0xff]
^ table[4][(high >> 24) & 0xff]
^ table[3][(high >> 32) & 0xff]
^ table[2][(high >> 40) & 0xff]
^ table[1][(high >> 48) & 0xff]
^ table[0][high >> 56];
next += 16;
length -= 16;
}
#else
while (length && ((uintptr_t)next & 3) != 0)
{
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
--length;
}
while (length >= 12)
{
crc ^= *(uint32_t *)next;
uint32_t high = *(uint32_t *)(next + 4);
uint32_t high2 = *(uint32_t *)(next + 8);
crc = table[11][crc & 0xff]
^ table[10][(crc >> 8) & 0xff]
^ table[9][(crc >> 16) & 0xff]
^ table[8][crc >> 24]
^ table[7][high & 0xff]
^ table[6][(high >> 8) & 0xff]
^ table[5][(high >> 16) & 0xff]
^ table[4][high >> 24]
^ table[3][high2 & 0xff]
^ table[2][(high2 >> 8) & 0xff]
^ table[1][(high2 >> 16) & 0xff]
^ table[0][high2 >> 24];
next += 12;
length -= 12;
}
#endif
while (length)
{
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
--length;
}
return (uint32_t)crc ^ 0xffffffff;
}
/* Apply the zeros operator table to crc. */
static inline uint32_t shift_crc(uint32_t shift_table[][256], uint32_t crc)
{
return shift_table[0][crc & 0xff]
^ shift_table[1][(crc >> 8) & 0xff]
^ shift_table[2][(crc >> 16) & 0xff]
^ shift_table[3][crc >> 24];
}
/* Compute CRC-32C using the Intel hardware instruction. */
extern "C" CRC32C_API uint32_t crc32c_append_hw(uint32_t crc, buffer buf, size_t len)
{
buffer next = buf;
buffer end;
#ifdef _M_X64
uint64_t crc0, crc1, crc2; /* need to be 64 bits for crc32q */
#else
uint32_t crc0, crc1, crc2;
#endif
/* pre-process the crc */
crc0 = crc ^ 0xffffffff;
/* compute the crc for up to seven leading bytes to bring the data pointer
to an eight-byte boundary */
while (len && ((uintptr_t)next & 7) != 0)
{
crc0 = _mm_crc32_u8(static_cast<uint32_t>(crc0), *next);
++next;
--len;
}
#ifdef _M_X64
/* compute the crc on sets of LONG_SHIFT*3 bytes, executing three independent crc
instructions, each on LONG_SHIFT bytes -- this is optimized for the Nehalem,
Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
throughput of one crc per cycle, but a latency of three cycles */
while (len >= 3 * LONG_SHIFT)
{
crc1 = 0;
crc2 = 0;
end = next + LONG_SHIFT;
do
{
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
crc1 = _mm_crc32_u64(crc1, *reinterpret_cast<const uint64_t *>(next + LONG_SHIFT));
crc2 = _mm_crc32_u64(crc2, *reinterpret_cast<const uint64_t *>(next + 2 * LONG_SHIFT));
next += 8;
} while (next < end);
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
next += 2 * LONG_SHIFT;
len -= 3 * LONG_SHIFT;
}
/* do the same thing, but now on SHORT_SHIFT*3 blocks for the remaining data less
than a LONG_SHIFT*3 block */
while (len >= 3 * SHORT_SHIFT)
{
crc1 = 0;
crc2 = 0;
end = next + SHORT_SHIFT;
do
{
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
crc1 = _mm_crc32_u64(crc1, *reinterpret_cast<const uint64_t *>(next + SHORT_SHIFT));
crc2 = _mm_crc32_u64(crc2, *reinterpret_cast<const uint64_t *>(next + 2 * SHORT_SHIFT));
next += 8;
} while (next < end);
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
next += 2 * SHORT_SHIFT;
len -= 3 * SHORT_SHIFT;
}
/* compute the crc on the remaining eight-byte units less than a SHORT_SHIFT*3
block */
end = next + (len - (len & 7));
while (next < end)
{
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
next += 8;
}
#else
/* compute the crc on sets of LONG_SHIFT*3 bytes, executing three independent crc
instructions, each on LONG_SHIFT bytes -- this is optimized for the Nehalem,
Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
throughput of one crc per cycle, but a latency of three cycles */
while (len >= 3 * LONG_SHIFT)
{
crc1 = 0;
crc2 = 0;
end = next + LONG_SHIFT;
do
{
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
crc1 = _mm_crc32_u32(crc1, *reinterpret_cast<const uint32_t *>(next + LONG_SHIFT));
crc2 = _mm_crc32_u32(crc2, *reinterpret_cast<const uint32_t *>(next + 2 * LONG_SHIFT));
next += 4;
} while (next < end);
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
next += 2 * LONG_SHIFT;
len -= 3 * LONG_SHIFT;
}
/* do the same thing, but now on SHORT_SHIFT*3 blocks for the remaining data less
than a LONG_SHIFT*3 block */
while (len >= 3 * SHORT_SHIFT)
{
crc1 = 0;
crc2 = 0;
end = next + SHORT_SHIFT;
do
{
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
crc1 = _mm_crc32_u32(crc1, *reinterpret_cast<const uint32_t *>(next + SHORT_SHIFT));
crc2 = _mm_crc32_u32(crc2, *reinterpret_cast<const uint32_t *>(next + 2 * SHORT_SHIFT));
next += 4;
} while (next < end);
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
next += 2 * SHORT_SHIFT;
len -= 3 * SHORT_SHIFT;
}
/* compute the crc on the remaining eight-byte units less than a SHORT_SHIFT*3
block */
end = next + (len - (len & 7));
while (next < end)
{
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
next += 4;
}
#endif
len &= 7;
/* compute the crc for up to seven trailing bytes */
while (len)
{
crc0 = _mm_crc32_u8(static_cast<uint32_t>(crc0), *next);
++next;
--len;
}
/* return a post-processed crc */
return static_cast<uint32_t>(crc0) ^ 0xffffffff;
}
extern "C" CRC32C_API int crc32c_hw_available()
{
int info[4];
__cpuid(info, 1);
return (info[2] & (1 << 20)) != 0;
}
void calculate_table()
{
for(int i = 0; i < 256; i++)
{
uint32_t res = (uint32_t)i;
for(int t = 0; t < 16; t++) {
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
table[t][i] = res;
}
}
_tableInitialized = true;
}
void calculate_table_hw()
{
for(int i = 0; i < 256; i++)
{
uint32_t res = (uint32_t)i;
for (int k = 0; k < 8 * (SHORT_SHIFT - 4); k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
for(int t = 0; t < 4; t++) {
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
short_shifts[3 - t][i] = res;
}
for (int k = 0; k < 8 * (LONG_SHIFT - 4 - SHORT_SHIFT); k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
for(int t = 0; t < 4; t++) {
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
long_shifts[3 - t][i] = res;
}
}
}
uint32_t (*append_func)(uint32_t, buffer, size_t);
void __crc32_init()
{
if (append_func == NULL)
{
// somebody can call sw version directly, so, precalculate table for this version
calculate_table();
if (crc32c_hw_available()) {
calculate_table_hw();
append_func = crc32c_append_hw;
} else {
append_func = crc32c_append_sw;
}
}
}
extern "C" CRC32C_API uint32_t crc32c_append(uint32_t crc, buffer input, size_t length)
{
if (append_func == NULL) {
__crc32_init();
}
return append_func(crc, input, length);
}

View file

@ -1,35 +0,0 @@
#ifndef CRC32C_H
#define CRC32C_H
#define CRC32C_API
#include <stdint.h>
/*
Computes CRC-32C (Castagnoli) checksum. Uses Intel's CRC32 instruction if it is available.
Otherwise it uses a very fast software fallback.
*/
extern "C" CRC32C_API uint32_t crc32c_append(
uint32_t crc, // Initial CRC value. Typically it's 0.
// You can supply non-trivial initial value here.
// Initial value can be used to chain CRC from multiple buffers.
const uint8_t *input, // Data to be put through the CRC algorithm.
size_t length); // Length of the data in the input buffer.
/*
Software fallback version of CRC-32C (Castagnoli) checksum.
*/
extern "C" CRC32C_API uint32_t crc32c_append_sw(uint32_t crc, const uint8_t *input, size_t length);
/*
Hardware version of CRC-32C (Castagnoli) checksum. Will fail, if CPU does not support related instructions. Use a crc32c_append version instead of.
*/
extern "C" CRC32C_API uint32_t crc32c_append_hw(uint32_t crc, const uint8_t *input, size_t length);
/*
Checks is hardware version of CRC-32C is available.
*/
extern "C" CRC32C_API int crc32c_hw_available();
#endif

View file

@ -1,42 +0,0 @@
#include "hasher.h"
#include "xxhash.h"
#include "crc32c.h"
#include <cstdio>
enum {
HASH_NONE = 0,
HASH_XXH3,
HASH_CRC32C
};
static int g_HashAlgorithm = HASH_NONE;
void InitHasher()
{
// Detect the best hashing algorithm to use for the host machine
// TODO/Future Improvement: This could be expanded to support even more hash algorithims
// And we could hash a random buffer to calculate the fastest hash to use on a given host
printf("Selecting hash algorithm: ");
if (crc32c_hw_available()) {
printf("CRC32C\n");
g_HashAlgorithm = HASH_CRC32C;
} else {
printf("XXH3\n");
g_HashAlgorithm = HASH_XXH3;
}
}
uint64_t ComputeHash(const void* data, size_t len)
{
if (g_HashAlgorithm == HASH_NONE) {
InitHasher();
}
switch (g_HashAlgorithm) {
case HASH_XXH3: return XXH3_64bits(data, len);
case HASH_CRC32C: return crc32c_append(0, (uint8_t*)data, len);
}
return 0;
}

View file

@ -27,8 +27,7 @@
#ifndef _HASHER_H
#define _HASHER_H
#include <stdint.h>
uint64_t ComputeHash(const void* data, size_t len);
#include "xxhash.h"
#define ComputeHash XXH3_64bits
#endif

View file

@ -25,7 +25,8 @@
// *
// ******************************************************************
#include "core\kernel\init\CxbxKrnl.h"
#include "cxbxr.hpp"
#include "core\kernel\support\Emu.h"
#include "EmuShared.h"
@ -83,8 +84,9 @@ bool EmuShared::Init(long long sessionID)
emuSharedStr.c_str() // name of map object
);
if(hMapObject == NULL)
return false; // CxbxKrnlCleanupEx(CXBXR_MODULE::INIT, "Could not map shared memory!");
if (hMapObject == NULL) {
CxbxrAbortEx(CXBXR_MODULE::INIT, "Could not map shared memory!");
}
if(GetLastError() == ERROR_ALREADY_EXISTS)
bRequireConstruction = false;
@ -105,7 +107,7 @@ bool EmuShared::Init(long long sessionID)
if (g_EmuShared == nullptr) {
CloseHandle(hMapObject);
return false; // CxbxKrnlCleanupEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!");
CxbxrAbortEx(CXBXR_MODULE::INIT, "Could not map view of shared memory!");
}
}
@ -152,6 +154,7 @@ EmuShared::EmuShared()
m_bEmulating_status = false;
m_bFirstLaunch = false;
m_bClipCursor = false;
m_LightgunLaser = 0xF; // laser on by default on all ports
std::memset(m_DeviceControlNames, '\0', sizeof(m_DeviceControlNames));
std::memset(m_DeviceName, '\0', sizeof(m_DeviceName));

View file

@ -246,10 +246,10 @@ class EmuShared : public Mutex
void SetLogPopupTestCase(const bool value) { Lock(); m_core.bLogPopupTestCase = value; Unlock(); }
// ******************************************************************
// * File storage location
// * Data location path
// ******************************************************************
void GetStorageLocation(char *path) { Lock(); strncpy(path, m_core.szStorageLocation, xbox::max_path); Unlock(); }
void SetStorageLocation(const char *path) { Lock(); strncpy(m_core.szStorageLocation, path, xbox::max_path); Unlock(); }
void GetDataLocation(char *path) { Lock(); strncpy(path, m_core.szStorageLocation, xbox::max_path); Unlock(); }
void SetDataLocation(const char *path) { Lock(); strncpy(m_core.szStorageLocation, path, xbox::max_path); Unlock(); }
// ******************************************************************
// * ClipCursor flag Accessors
@ -257,6 +257,12 @@ class EmuShared : public Mutex
void GetClipCursorFlag(bool *value) { Lock(); *value = m_bClipCursor; Unlock(); }
void SetClipCursorFlag(const bool value) { Lock(); m_bClipCursor = value; Unlock(); }
// ******************************************************************
// * LightgunLaser flag Accessors
// ******************************************************************
void GetLightgunLaser(uint8_t *value, int port) { Lock(); *value = (m_LightgunLaser >> port) & 1; Unlock(); }
void SetLightgunLaser(const uint8_t *value, int port) { Lock(); (m_LightgunLaser &= ~(1 << port)) |= ((*value) << port); Unlock(); }
// ******************************************************************
// * ImGui Accessors
// ******************************************************************
@ -266,7 +272,7 @@ class EmuShared : public Mutex
void GetImGuiIniSettings(char value[IMGUI_INI_SIZE_MAX]) {
Lock();
if (m_imgui_general.ini_size < IMGUI_INI_SIZE_MAX) {
value = '\0';
value[0] = '\0';
return;
}
strcpy_s(value, IMGUI_INI_SIZE_MAX, m_imgui_general.ini_settings);
@ -364,6 +370,7 @@ class EmuShared : public Mutex
#else
unsigned int m_Reserved;
#endif
uint8_t m_LightgunLaser;
bool m_bFirstLaunch;
bool m_bClipCursor;
unsigned int m_dwKrnlProcID; // Only used for kernel mode level.

View file

@ -30,7 +30,6 @@
#include <windows.h>
#include "Cxbx.h"
#include "core\kernel\init\CxbxKrnl.h"
#include "gui/resource/ResCxbx.h"
#include "common\IPCHybrid.hpp"
@ -38,6 +37,9 @@
#include "common\Settings.hpp"
#include "Logging.h"
#ifdef CXBXR_EMU
/*! parent window handle */
extern "C" HWND CxbxKrnl_hEmuParent = NULL;
void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value)
{
@ -79,6 +81,7 @@ void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value)
SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, MAKEWPARAM(WM_COMMAND, cmdParam), value);
}
}
#endif
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd)
{
@ -98,6 +101,10 @@ void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const un
cmdParam = ID_SYNC_CONFIG_INPUT;
break;
case IPC_UPDATE_KERNEL::CONFIG_CHANGE_TIME:
cmdParam = ID_SYNC_TIME_CHANGE;
break;
default:
cmdParam = 0;
break;

View file

@ -27,11 +27,13 @@
#if defined(_WIN32) || defined(WIN32)
#include "core\kernel\init\CxbxKrnl.h"
#include <windows.h>
#include <optional>
#include "common/util/cliConfig.hpp"
#include "Util.h"
// Source: https://stackoverflow.com/questions/8046097/how-to-check-if-a-process-has-the-administrative-rights
bool CxbxIsElevated() {
bool CxbxrIsElevated() {
bool fRet = false;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
@ -47,13 +49,13 @@ bool CxbxIsElevated() {
return fRet;
}
bool CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) {
std::optional<std::string> CxbxrExec(bool useDebugger, void** hProcess, bool requestHandleProcess) {
STARTUPINFO startupInfo = { 0 };
PROCESS_INFORMATION processInfo = { 0 };
std::string szProcArgsBuffer;
if (!cli_config::GenCMD(szProcArgsBuffer)) {
return false;
return std::make_optional<std::string>("Failed to retrieve the command line arguments to launch the new emulation process");
}
// TODO: Set a configuration variable for this. For now it will be within the same folder as Cxbx.exe
@ -67,9 +69,12 @@ bool CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) {
Using ShellExecute has proper implement. Unfortunately, we need created process handle for Debugger monitor.
Plus ShellExecute is high level whilst CreateProcess is low level. We want to use official low level functions as possible to reduce
cpu load cycles to get the task done.
Without the DETACHED_PROCESS flag, the default behavior would be for the new process to inherit the console of the parent process,
which is wrong since we want the emulation process to have its own console allocated with AllocConsole instead.
*/
if (CreateProcess(nullptr, const_cast<LPSTR>(szProcArgsBuffer.c_str()), nullptr, nullptr, false, 0, nullptr, nullptr, &startupInfo, &processInfo) == 0) {
return 0;
if (CreateProcess(nullptr, const_cast<LPSTR>(szProcArgsBuffer.c_str()), nullptr, nullptr, false, DETACHED_PROCESS, nullptr, nullptr, &startupInfo, &processInfo) == 0) {
return std::make_optional<std::string>("Failed to create the new emulation process. CreateProcess failed because: " + WinError2Str());
}
CloseHandle(processInfo.hThread);
@ -80,7 +85,7 @@ bool CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) {
CloseHandle(processInfo.hProcess);
}
return 1;
return std::nullopt;
}
#endif

View file

@ -151,12 +151,16 @@ public:
cpuSets.erase(cpuSets.begin());
}
// Otherwise: Multiple physical cores
// Assign the first logical and physical core to Xbox, leave the rest of that
// physical core unassigned (if hyperthreading is active), the remaining
// physical cores to other threads
// Assign the first highest performance logical and physical core to Xbox,
// leave the rest of that physical core unassigned (if hyperthreading is active),
// give the remaining physical cores to other threads
else {
const BYTE physicalCore = cpuSets[0]->CoreIndex;
m_xboxCPUSet = cpuSets[0]->Id;
auto highPerfCore = std::max_element(cpuSets.begin(), cpuSets.end(), [](const auto* left, const auto* right)
{
return left->EfficiencyClass < right->EfficiencyClass;
});
const BYTE physicalCore = (*highPerfCore)->CoreIndex;
m_xboxCPUSet = (*highPerfCore)->Id;
for (auto it = cpuSets.begin(); it != cpuSets.end(); ) {
if ((*it)->CoreIndex == physicalCore) {
it = cpuSets.erase(it);
@ -196,7 +200,7 @@ class Win7Policy final : public AffinityPolicy
public:
bool Initialize() {
if (!GetProcessAffinityMask(g_CurrentProcessHandle, &CPUXbox, &CPUOthers))
CxbxKrnlCleanupEx(CXBXR_MODULE::INIT, "GetProcessAffinityMask failed.");
CxbxrAbortEx(CXBXR_MODULE::INIT, "GetProcessAffinityMask failed.");
// For the other threads, remove one bit from the processor mask:
CPUOthers = ((CPUXbox - 1) & CPUXbox);

45
src/common/win32/Util.cpp Normal file
View file

@ -0,0 +1,45 @@
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#include "Util.h"
#include "Windows.h"
std::string WinError2Str()
{
DWORD error_id = GetLastError();
if (error_id == ERROR_SUCCESS) {
return std::string();
}
LPSTR msg_buff = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPSTR>(&msg_buff), 0, nullptr);
std::string msg(msg_buff, size);
LocalFree(msg_buff);
return msg;
}

30
src/common/win32/Util.h Normal file
View file

@ -0,0 +1,30 @@
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
#include <string>
std::string WinError2Str();

View file

@ -34,7 +34,6 @@
#include <locale> // For ctime
#include <array>
#include "devices\LED.h" // For LED::Sequence
#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlPrintUEM
#include "common\crypto\EmuSha.h" // For the SHA functions
#include "common\crypto\EmuRsa.h" // For the RSA functions
#include "core\hle\XAPI\Xapi.h" // For LDT_FROM_DASHBOARD
@ -42,12 +41,12 @@
#include "common/AddressRanges.h"
#include "common/xbox/Types.hpp"
namespace fs = std::filesystem;
#ifdef CXBXR_EMU
extern "C" void CxbxKrnlPrintUEM(ULONG);
#endif
// construct via Xbe file
Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
Xbe::Xbe(const char *x_szFilename)
{
char szBuffer[MAX_PATH];
@ -57,22 +56,23 @@ Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
FILE *XbeFile = fopen(x_szFilename, "rb");
// verify Xbe file was opened successfully
if(XbeFile == 0)
{
using namespace fs; // limit its scope inside here
std::string XbeName = std::filesystem::path(x_szFilename).filename().string(); // recover the xbe name
std::string XbeName = path(x_szFilename).filename().string(); // recover the xbe name
// verify Xbe file was opened successfully
if(XbeFile == 0) {
// NOTE: the check for the existence of the child window is necessary because the user could have previously loaded the dashboard,
// removed/changed the path and attempt to load it again from the recent list, which will crash CxbxInitWindow below
// Note that GetHwnd(), CxbxKrnl_hEmuParent and HalReturnToFirmware are all not suitable here for various reasons
if (XbeName.compare(std::string("xboxdash.xbe")) == 0 && !bFromGUI)
{
#ifdef CXBXR_EMU
if (XbeName.compare(std::string("xboxdash.xbe")) == 0) {
// The dashboard could not be found on partition2. This is a fatal error on the Xbox so we display the UEM. The
// error code is different if we have a launch data page
CxbxInitWindow(false);
// This is necessary because CxbxInitWindow internally calls g_AffinityPolicy->SetAffinityOther. If we are launched directly from the command line and the dashboard
// cannot be opened, we will crash below because g_AffinityPolicy will be empty
g_AffinityPolicy = AffinityPolicy::InitPolicy();
CxbxInitWindow();
ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC;
@ -86,12 +86,14 @@ Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
// TODO: FATAL_ERROR_XBE_DASH_X2_PASS (requires DVD drive authentication emulation...)
}
else
{
else {
#endif
// Report which xbe could not be found
SetFatalError(std::string("Could not open the Xbe file ") + XbeName);
return;
#ifdef CXBXR_EMU
}
#endif
}
printf("OK\n");
@ -109,6 +111,14 @@ Xbe::Xbe(const char *x_szFilename, bool bFromGUI)
printf("OK\n");
// remember the Xbe file name
{
printf("Xbe::Xbe: Storing Xbe File Name...");
strncpy(m_szFileName, XbeName.c_str(), ARRAY_SIZE(m_szFileName) * sizeof(char));
}
printf("OK\n");
// read Xbe image header
{
printf("Xbe::Xbe: Reading Image Header...");
@ -722,25 +732,29 @@ void Xbe::PurgeBadChar(std::string& s, const std::string& illegalChars)
}
}
const char *Xbe::GameRegionToString()
const char *Xbe::GameRegionToString(uint32_t dwRegionFlags)
{
if (!dwRegionFlags) {
dwRegionFlags = m_Certificate.dwGameRegion;
}
const char *Region_text[] = {
"Unknown", "NTSC", "JAP", "NTSC+JAP",
"PAL", "PAL+NTSC", "PAL+JAP", "Region Free",
"DEBUG", "NTSC (DEBUG)", "JAP (DEBUG)", "NTSC+JAP (DEBUG)",
"PAL (DEBUG)", "PAL+NTSC (DEBUG)", "PAL+JAP (DEBUG)", "Region Free (DEBUG)"
"Unknown", "NTSC", "JAPAN", "NTSC+JAPAN",
"PAL", "PAL+NTSC", "PAL+JAPAN", "Region Free",
"DEBUG", "NTSC (DEBUG)", "JAPAN (DEBUG)", "NTSC+JAPAN (DEBUG)",
"PAL (DEBUG)", "PAL+NTSC (DEBUG)", "PAL+JAPAN (DEBUG)", "Region Free (DEBUG)"
};
const uint32_t all_regions = XBEIMAGE_GAME_REGION_NA |
XBEIMAGE_GAME_REGION_JAPAN |
XBEIMAGE_GAME_REGION_RESTOFWORLD |
XBEIMAGE_GAME_REGION_MANUFACTURING;
if(m_Certificate.dwGameRegion & ~all_regions) {
if(dwRegionFlags & ~all_regions) {
return "REGION ERROR";
}
uint8_t index = (m_Certificate.dwGameRegion & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
index |= (m_Certificate.dwGameRegion & 0x7);
uint8_t index = (dwRegionFlags & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
index |= (dwRegionFlags & 0x7);
return Region_text[index];
}
@ -815,5 +829,15 @@ XbeType Xbe::GetXbeType()
return XbeType::xtRetail;
}
uint32_t Xbe::GetDiscVersion()
{
return m_Certificate.dwVersion & 0xFF;
}
uint32_t Xbe::GetPatchVersion()
{
return (m_Certificate.dwVersion & 0xFFFFFF00) >> 8;
}
template auto Xbe::FindSection<true>(const char *zsSectionName);
template auto Xbe::FindSection<false>(const char *zsSectionName);

View file

@ -28,6 +28,7 @@
#include "common\Error.h"
#include "common/xbox/Types.hpp"
#include "core/kernel/common/types.h"
#include <cstdio>
@ -49,7 +50,7 @@ class Xbe : public Error
{
public:
// construct via Xbe file
Xbe(const char *x_szFilename, bool bFromGUI);
Xbe(const char *x_szFilename);
// deconstructor
~Xbe();
@ -80,10 +81,13 @@ class Xbe : public Error
void PurgeBadChar(std::string& s, const std::string& illegalChars = "\\/:?\"<>|");
// Convert game region field to string
const char *GameRegionToString();
const char *GameRegionToString(uint32_t dwRegionFlags = 0);
XbeType GetXbeType();
uint32_t GetDiscVersion();
uint32_t GetPatchVersion();
// Xbe header
#include "AlignPrefix1.h"
struct Header
@ -156,7 +160,7 @@ class Xbe : public Error
uint32_t dwAllowedMedia; // 0x009C - allowed media types
uint32_t dwGameRegion; // 0x00A0 - game region
uint32_t dwGameRatings; // 0x00A4 - game ratings
uint32_t dwDiskNumber; // 0x00A8 - disk number
uint32_t dwDiscNumber; // 0x00A8 - disc number
uint32_t dwVersion; // 0x00AC - version
uint8_t bzLanKey[16]; // 0x00B0 - lan key
uint8_t bzSignatureKey[16]; // 0x00C0 - signature key
@ -257,6 +261,9 @@ class Xbe : public Error
// Xbe original path
char m_szPath[MAX_PATH];
// Xbe original file name
char m_szFileName[MAX_PATH];
// Xbe ascii title, translated from certificate title
char m_szAsciiTitle[41];

View file

@ -33,8 +33,6 @@
#include <iomanip> // For std::setfill, std::uppercase, std::hex
#include "common/util/strConverter.hpp" // for utf16le_to_ascii
extern std::string FormatTitleId(uint32_t title_id); // Exposed in Emu.cpp
// better time
static char *BetterTime(uint32_t x_timeDate)
{
@ -60,6 +58,34 @@ std::string DumpInformation(Xbe* Xbe_object)
}
#define SSTREAM_SET_HEX(stream_name) stream_name << std::setfill('0') << std::uppercase << std::hex;
#define SSTREAM_SET_DEC(stream_name) stream_name << std::setfill('0') << std::uppercase << std::dec;
std::string FormatTitleId(uint32_t title_id)
{
std::stringstream ss;
// If the Title ID prefix is a printable character, parse it
// This shows the correct game serial number for retail titles!
// EG: MS-001 for 1st tile published by MS, EA-002 for 2nd title by EA, etc
// Some special Xbes (Dashboard, XDK Samples) use non-alphanumeric serials
// We fall back to Hex for those
// ergo720: we cannot use isalnum() here because it will treat chars in the range -1 - 255 as valid ascii chars which can
// lead to unicode characters being printed in the title (e.g.: dashboard uses 0xFE and 0xFF)
uint8_t pTitleId1 = (title_id >> 24) & 0xFF;
uint8_t pTitleId2 = (title_id >> 16) & 0xFF;
if ((pTitleId1 < 65 || pTitleId1 > 90) || (pTitleId2 < 65 || pTitleId2 > 90)) {
// Prefix was non-printable, so we need to print a hex reprentation of the entire title_id
ss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase << title_id;
return ss.str();
}
ss << pTitleId1 << pTitleId2;
ss << "-";
ss << std::setfill('0') << std::setw(3) << std::dec << (title_id & 0x0000FFFF);
return ss.str();
}
XbePrinter::XbePrinter(Xbe* Xbe_object)
{
@ -334,8 +360,12 @@ std::string XbePrinter::GenMediaInfo()
text << "Allowed Media : 0x" << std::setw(8) << Xbe_certificate->dwAllowedMedia << " (" << AllowedMediaToString() << ")\n";
text << "Game Region : 0x" << std::setw(8) << Xbe_certificate->dwGameRegion << " (" << Xbe_to_print->GameRegionToString() << ")\n";
text << "Game Ratings : 0x" << std::setw(8) << Xbe_certificate->dwGameRatings << " (" << GameRatingToString() << ")\n";
text << "Disk Number : 0x" << std::setw(8) << Xbe_certificate->dwDiskNumber << "\n";
text << "Version : 1." << std::dec << std::setw(2) << Xbe_certificate->dwVersion << "\n";
text << "Disc Number : 0x" << std::setw(8) << Xbe_certificate->dwDiscNumber << "\n";
text << "Version : 0x" << std::setw(8) << Xbe_certificate->dwVersion << "\n";
SSTREAM_SET_DEC(text);
text << "Disc Version : " << std::setw(0) << Xbe_to_print->GetDiscVersion() << "\n";
text << "Patch Version : " << std::setw(0) << Xbe_to_print->GetPatchVersion() << "\n";
return text.str();
}

View file

@ -30,6 +30,8 @@
#include <cstdio>
extern std::string FormatTitleId(uint32_t title_id);
extern std::string DumpInformation(Xbe* Xbe_object);
class XbePrinter

View file

@ -25,6 +25,7 @@ void ImGuiAudio::DrawMenu()
{
if (ImGui::BeginMenu("Audio")) {
ImGui::MenuItem("Debug General Cache Stats", NULL, &m_windows.cache_stats_general);
ImGui::MenuItem("SoundBuffer Visualization", NULL, &m_windows.cache_visualization);
ImGui::EndMenu();
}
}
@ -34,4 +35,7 @@ void ImGuiAudio::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
//TODO: In need of make interface class to return generic info in some way.
extern void DSound_PrintStats(bool, ImGuiWindowFlags, bool m_show_audio_stats);
DSound_PrintStats(is_focus, input_handler, m_windows.cache_stats_general);
extern void DSound_DrawBufferVisualization(bool, bool *p_show, ImGuiWindowFlags);
DSound_DrawBufferVisualization(is_focus, &m_windows.cache_visualization, input_handler);
}

View file

@ -27,8 +27,11 @@
// Intended to store as permanent settings
typedef struct {
bool build_hash;
bool fps;
bool hle_lle_stats;
bool title_name;
bool file_name;
} overlay_settings;
// Intended for EmuShared only below
@ -44,6 +47,7 @@ typedef struct {
typedef struct {
bool cache_stats_general;
bool cache_visualization;
bool Reserved[3];
} imgui_audio_windows;

View file

@ -20,14 +20,14 @@ bool ImGuiUI::Initialize()
IMGUI_CHECKVERSION();
m_imgui_context = ImGui::CreateContext();
if (!m_imgui_context) {
CxbxKrnlCleanup("Unable to create ImGui context!");
CxbxrAbort("Unable to create ImGui context!");
return false;
}
ImGuiIO& io = ImGui::GetIO();
#if 0 // TODO: Currently most voted for memory, so this block of code is disabled. And may will add an option between file vs memory.
// May be best ideal to do manual update call than ImGui's internal auto update.
g_EmuShared->GetStorageLocation(m_file_path);
g_EmuShared->GetDataLocation(m_file_path);
if (m_file_path[0] == '\0') {
return false;
}
@ -45,6 +45,11 @@ bool ImGuiUI::Initialize()
g_EmuShared->GetOverlaySettings(&m_settings);
g_EmuShared->GetFlagsLLE(&m_lle_flags);
// Internal initialize (when necessary, move into its own function.)
fps_counter = 30.0f;
// Miscs
m_audio.Initialize();
m_video.Initialize();
@ -56,7 +61,7 @@ void ImGuiUI::Shutdown()
size_t ini_size = IMGUI_INI_SIZE_MAX;
const char* temp_ini_settings = ImGui::SaveIniSettingsToMemory(&ini_size);
if (ini_size > IMGUI_INI_SIZE_MAX) {
CxbxKrnlCleanup("ImGui ini settings is too large: %d > %d (IMGUI_INI_SIZE_MAX)", ini_size, IMGUI_INI_SIZE_MAX);
CxbxrAbort("ImGui ini settings is too large: %d > %d (IMGUI_INI_SIZE_MAX)", ini_size, IMGUI_INI_SIZE_MAX);
}
g_EmuShared->SetImGuiIniSettings(temp_ini_settings);
g_EmuShared->SetOverlaySettings(&m_settings);
@ -76,6 +81,36 @@ void ImGuiUI::ToggleImGui()
g_EmuShared->SetImGuiFocusFlag(m_is_focus);
}
static clock_t g_DeltaTime = 0; // Used for benchmarking/fps count
static unsigned int g_Frames = 0;
// ******************************************************************
// * update the current milliseconds per frame
// ******************************************************************
void ImGuiUI::UpdateCurrentMSpFAndFPS() {
if (g_EmuShared) {
fps_counter = (float)(g_Frames * 0.5 + fps_counter * 0.5);
g_EmuShared->SetCurrentFPS(&fps_counter);
}
}
void ImGuiUI::UpdateFPSCounter()
{
static clock_t lastDrawFunctionCallTime = 0;
clock_t currentDrawFunctionCallTime = clock();
g_DeltaTime += currentDrawFunctionCallTime - lastDrawFunctionCallTime;
lastDrawFunctionCallTime = currentDrawFunctionCallTime;
g_Frames++;
if (g_DeltaTime >= CLOCKS_PER_SEC) {
UpdateCurrentMSpFAndFPS();
g_Frames = 0;
g_DeltaTime -= CLOCKS_PER_SEC;
}
}
void ImGuiUI::DrawMenu()
{
if (!m_is_focus) {
@ -85,8 +120,11 @@ void ImGuiUI::DrawMenu()
if (ImGui::BeginMenu("Settings")) {
if (ImGui::BeginMenu("Overlay")) {
bool bChanged = false;
bChanged |= ImGui::MenuItem("Show Build Hash", NULL, &m_settings.build_hash);
bChanged |= ImGui::MenuItem("Show FPS", NULL, &m_settings.fps);
bChanged |= ImGui::MenuItem("Show HLE/LLE Stats", NULL, &m_settings.hle_lle_stats);
bChanged |= ImGui::MenuItem("Show Title Name", NULL, &m_settings.title_name);
bChanged |= ImGui::MenuItem("Show File Name", NULL, &m_settings.file_name);
if (bChanged) {
g_EmuShared->SetOverlaySettings(&m_settings);
ipc_send_gui_update(IPC_UPDATE_GUI::OVERLAY, 1);
@ -104,21 +142,20 @@ void ImGuiUI::DrawMenu()
void ImGuiUI::DrawWidgets()
{
if (m_settings.fps || m_settings.hle_lle_stats) {
constexpr auto overlay_window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing;
bool overlay_show_topright = m_settings.fps
|| m_settings.hle_lle_stats
|| m_settings.title_name
|| m_settings.file_name;
if (overlay_show_topright) {
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - (IMGUI_MIN_DIST_SIDE/* * m_backbuffer_scale*/),
IMGUI_MIN_DIST_TOP/* * m_backbuffer_scale*/), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
ImGui::SetNextWindowSize(ImVec2(200.0f/* * m_backbuffer_scale*/, 0.0f));
ImGui::SetNextWindowBgAlpha(0.5f);
if (ImGui::Begin("overlay_stats", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) {
if (ImGui::Begin("overlay_stats_topright", nullptr, overlay_window_flags)) {
if (m_settings.fps) {
float fps_counter;
g_EmuShared->GetCurrentFPS(&fps_counter);
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "FPS: %.2f MS / F : %.2f", fps_counter, (float)(1000.0 / fps_counter));
}
@ -148,8 +185,28 @@ void ImGuiUI::DrawWidgets()
- ImGui::GetScrollX() - 2 * ImGui::GetStyle().ItemSpacing.x);
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), flagString.c_str());
}
if (m_settings.title_name) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Title: %.41s", CxbxKrnl_Xbe->m_szAsciiTitle);
}
if (m_settings.file_name) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "File: %.260s", CxbxKrnl_Xbe->m_szFileName);
}
}
ImGui::End();
}
if (m_settings.build_hash) {
ImGui::SetNextWindowPos(ImVec2(IMGUI_MIN_DIST_SIDE, ImGui::GetIO().DisplaySize.y - IMGUI_MIN_DIST_SIDE/* * m_backbuffer_scale*/),
ImGuiCond_Always, ImVec2(0.0f, 1.0f));
ImGui::SetNextWindowBgAlpha(0.5f);
if (ImGui::Begin("overlay_stats_bottom", nullptr, overlay_window_flags)) {
if (m_settings.build_hash) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Build: %s", GetGitVersionStr());
}
}
ImGui::End();
}
ImGuiWindowFlags input_handler = m_is_focus ? ImGuiWindowFlags_None : ImGuiWindowFlags_NoInputs;

View file

@ -28,6 +28,7 @@ public:
void ToggleImGui();
bool IsImGuiFocus();
void UpdateFPSCounter();
void DrawMenu();
void DrawWidgets();
@ -48,6 +49,8 @@ protected:
callback(this, arg);
}
void UpdateCurrentMSpFAndFPS();
std::mutex m_imgui_mutex;
ImGuiContext* m_imgui_context;
char m_file_path[FILENAME_MAX+1];
@ -57,6 +60,7 @@ protected:
ImGuiVideo m_video;
overlay_settings m_settings;
unsigned int m_lle_flags;
float fps_counter;
// Make them as settings storage.
/*bool m_show_fps;
bool m_show_LLE_stats;

View file

@ -14,6 +14,13 @@
#include "core/kernel/init/CxbxKrnl.h"
#include "core/hle/D3D8/XbVertexBuffer.h"
const ImColor ImGuiVideo::m_laser_col[4] = {
ImColor(ImVec4(1.0f, 0.0f, 0.0f, 1.0f)), // player1: red
ImColor(ImVec4(0.0f, 1.0f, 0.0f, 1.0f)), // player2: green
ImColor(ImVec4(0.0f, 0.0f, 1.0f, 1.0f)), // player3: blue
ImColor(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)) // player4: yellow
};
bool ImGuiVideo::Initialize()
{
g_EmuShared->GetImGuiVideoWindows(&m_windows);
@ -43,6 +50,15 @@ void ImGuiVideo::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
if (ImGui::CollapsingHeader("Vertex Buffer Cache", ImGuiTreeNodeFlags_DefaultOpen)) {
VertexBufferConverter.DrawCacheStats();
}
}
ImGui::End();
}
// Render the lightgun laser
for (int port = PORT_1; port < XBOX_NUM_PORTS; ++port) {
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN && g_devs[port].info.ligthgun.laser) {
ImGui::Begin("Laser", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoDecoration);
ImGui::GetForegroundDrawList()->AddCircleFilled(g_InputDeviceManager.CalcLaserPos(port), 5, m_laser_col[port], 0);
ImGui::End();
}
}

View file

@ -20,4 +20,5 @@ public:
protected:
imgui_video_windows m_windows;
static const ImColor m_laser_col[4];
};

View file

@ -1,6 +1,3 @@
// This starts the raw string (comment to get syntax highlighting, UNCOMMENT to compile) :
R"DELIMITER(
struct PS_INPUT // Declared identical to vertex shader output (see VS_OUTPUT)
{
float2 iPos : VPOS; // Screen space x,y pixel location
@ -54,7 +51,8 @@ uniform const float4 FC1 : register(c17); // Note : Maps to PSH_XBOX_CONSTANT_FC
uniform const float4 BEM[4] : register(c19); // Note : PSH_XBOX_CONSTANT_BEM for 4 texture stages
uniform const float4 LUM[4] : register(c23); // Note : PSH_XBOX_CONSTANT_LUM for 4 texture stages
uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTANT_LUM for 4 texture stages
uniform const float4 FOGINFO : register(c28);
uniform const float FOGENABLE : register(c29);
#define CM_LT(c) if(c < 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_LT
#define CM_GE(c) if(c >= 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_GE
@ -92,10 +90,9 @@ uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTA
#define PS_FINALCOMBINERSETTING_CLAMP_SUM
#endif
)DELIMITER", /* This terminates the 1st raw string within the 16380 single-byte characters limit. // */
// See https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?f1url=%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C2026)%26rd%3Dtrue&view=vs-2019
// Second raw string :
R"DELIMITER(
// Hardcoded state will be inserted here
// <HARDCODED STATE GOES HERE>
// End hardcoded state
// PS_COMBINERCOUNT_UNIQUE_C0 steers whether for C0 to use combiner stage-specific constants c0_0 .. c0_7, or c0_0 for all stages
#ifdef PS_COMBINERCOUNT_UNIQUE_C0
@ -117,7 +114,7 @@ R"DELIMITER(
#ifdef PS_COMBINERCOUNT_MUX_MSB
#define FCS_MUX (r0.a >= 0.5) // Check r0.a MSB; Having range upto 1 this should be equal to : (((r0.a * 255) /*mod 256*/) >= 128)
#else // PS_COMBINERCOUNT_MUX_LSB
#define FCS_MUX (((r0.a * 255) mod 2) >= 1) // Check r0.b LSB; Get LSB by converting 1 into 255 (highest 8-bit value) and using modulo 2. TODO : Verify correctness
#define FCS_MUX (((r0.a * 255) % 2) >= 1) // Check r0.b LSB; Get LSB by converting 1 into 255 (highest 8-bit value) and using modulo 2. TODO : Verify correctness
#endif
// PS_FINALCOMBINERSETTING_COMPLEMENT_V1, when defined, applies a modifier to the v1 input when calculating the sum register
@ -141,15 +138,17 @@ R"DELIMITER(
#define FCS_SUM s_ident // otherwise identity mapping. TODO : Confirm correctness
#endif
#define xdot(s0, s1) dot((s0).rgb, (s1).rgb)
// Xbox supports only one 'pixel shader' opcode, but bit flags tunes it's function;
// Here, effective all 5 Xbox opcodes, extended with a variable macro {xop_m(m,...)} for destination modifier :
// Note : Since both d0 AND d1 could be the same output register, calculation of d2 can re-use only one (d0 or d1)
#define xmma(d0, d1, d2, s0, s1, s2, s3, m, tmp) tmp = d0 = m(s0 * s1); d1 = m(s2 * s3); d2 = d1 + tmp // PS_COMBINEROUTPUT_AB_CD_SUM= 0x00L, // 3rd output is AB+CD
#define xmmc(d0, d1, d2, s0, s1, s2, s3, m, tmp) tmp = d0 = m(s0 * s1); d1 = m(s2 * s3); d2 = FCS_MUX ? d1 : tmp // PS_COMBINEROUTPUT_AB_CD_MUX= 0x04L, // 3rd output is MUX(AB,CD) based on R0.a
#define xdm(d0, d1, s0, s1, s2, s3, m) d0 = m(dot(s0 , s1)); d1 = m( s2 * s3 ) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L,
#define xdd(d0, d1, s0, s1, s2, s3, m) d0 = m(dot(s0 , s1)); d1 = m(dot(s2 , s3)) // PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only // PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L,
#define xmd(d0, d1, s0, s1, s2, s3, m) d0 = m( s0 * s1 ); d1 = m(dot(s2 , s3)) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x01L,
#define xdm(d0, d1, s0, s1, s2, s3, m) d0 = m(xdot(s0 , s1)); d1 = m( s2 * s3 ) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L,
#define xdd(d0, d1, s0, s1, s2, s3, m) d0 = m(xdot(s0 , s1)); d1 = m(xdot(s2 , s3)) // PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only // PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L,
#define xmd(d0, d1, s0, s1, s2, s3, m) d0 = m( s0 * s1 ); d1 = m(xdot(s2 , s3)) // PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only // PS_COMBINEROUTPUT_CD_MULTIPLY= 0x01L,
// After the register combiner stages, there's one (optional) final combiner step, consisting of 4 parts;
// All the 7 final combiner inputs operate on rgb only and clamp negative input to zero:
@ -171,10 +170,6 @@ R"DELIMITER(
// HLSL : https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp
// lerp(x, y, s ) x*(1-s ) + y*s == x + s(y-x)
// lerp(s2, s1, s0) s2*(1-s0) + s1*s0
)DELIMITER", /* This terminates the 1st raw string within the 16380 single-byte characters limit. // */
// See https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?f1url=%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C2026)%26rd%3Dtrue&view=vs-2019
// Second raw string :
R"DELIMITER(
float m21d(const float input)
{
@ -203,20 +198,39 @@ float m21(const float input)
return (float)tmp / 127; // -128 scales to -1.007874016, 0 scales to 0.0, 127 scales to 1.0
}
float hls(float input) // 0..65535 range
{
float tmp = (float)(input);
tmp = (input < 32768) ? tmp / 32767 : (tmp - 65536) / 32767; // -1..1
return (float)tmp;
}
float hlu(float input) // 0..65535 range
{
return (float)input / 65535; // 0..1
}
float p2(float input) // power of 2
{
return input * input;
}
// Note : each component seems already in range [0..1], but two must be combined into one
#define TwoIntoOne(a,b) (((a * 255) * 256) + (b * 255)) / 255 // TODO : Verify whether this works at all !
#define TwoIntoOne(a,b) (((a * 256) + b) * 255)
#define CalcHiLo(in) H = TwoIntoOne(in.x, in.y); L = TwoIntoOne(in.z, in.w) // TODO : Verify whether this works at all !
// Dot mappings over the output value of a (4 component 8 bit unsigned) texture stage register into a (3 component float) vector value, for use in a dot product calculation:
#define PS_DOTMAPPING_ZERO_TO_ONE(in) dm = in.rgb // :r8g8b8a8->(r,g,b): 0x00=>0, 0xff=>1 thus : output = (input / 0xff )
#define PS_DOTMAPPING_MINUS1_TO_1_D3D(in) dm = float3(m21d(in.x), m21d(in.y), m21d(in.z)) // :r8g8b8a8->(r,g,b): 0x00=>-128/127, 0x01=>-1, 0x80=>0, 0xff=>1 thus : output = ((input - 0x100 ) / 0x7f )
#define PS_DOTMAPPING_MINUS1_TO_1_GL(in) dm = float3(m21g(in.x), m21g(in.y), m21g(in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x80 ) (see https://en.wikipedia.org/wiki/Two's_complement)
#define PS_DOTMAPPING_MINUS1_TO_1(in) dm = float3(m21(in.x), m21(in.y), m21(in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-128/127, ?0x81=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x7f ) (see https://en.wikipedia.org/wiki/Two's_complement)
#define PS_DOTMAPPING_MINUS1_TO_1(in) dm = float3(m21( in.x), m21( in.y), m21( in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-128/127, ?0x81=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x7f ) (see https://en.wikipedia.org/wiki/Two's_complement)
#define PS_DOTMAPPING_HILO_1(in) CalcHiLo(in); dm = float3(H, L, 1) // :H16L16 ->(H,L,1): 0x0000=>0, 0xffff=>1 thus : output = (input / 0xffff)
#define PS_DOTMAPPING_HILO_HEMISPHERE_D3D(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=32767/32768 thus : output = ((input - 0x10000) / 0x7fff)
#define PS_DOTMAPPING_HILO_HEMISPHERE_GL(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x8000)
#define PS_DOTMAPPING_HILO_HEMISPHERE(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)): 0x8000=>-32768/32767, 0x8001=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x7fff)
#define PS_DOTMAPPING_HILO_1(in) CalcHiLo(in); dm = float3(hlu(H), hlu(L), 1) // :H16L16 ->(H,L,1): 0x0000=>0, 0xffff=>1 thus : output = (input / 0xffff)
#define PS_DOTMAPPING_HILO_HEMISPHERE_D3D(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=32767/32768 thus : output = ((input - 0x10000) / 0x7fff)
#define PS_DOTMAPPING_HILO_HEMISPHERE_GL(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x8000)
#define PS_DOTMAPPING_HILO_HEMISPHERE(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)): 0x8000=>-32768/32767, 0x8001=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x7fff)
// Declare one sampler per each {Sampler Type, Texture Stage} combination
// TODO : Generate sampler status?
@ -310,7 +324,7 @@ float3 DoBumpEnv(const float4 TexCoord, const float4 BumpEnvMat, const float4 sr
/*--23 texbrdf */ #define PS_TEXTUREMODES_BRDF(ts) s = Brdf(ts); v = Sample3D(ts, s); t[ts] = v // TODO : Test (t[ts-2] is 16 bit eyePhi,eyeSigma; t[ts-1] is lightPhi,lightSigma)
/*--23 texm3x2tex */ #define PS_TEXTUREMODES_DOT_ST(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample2D(ts, s); t[ts] = v // TODO : Test
/*--23 texm3x2depth */ #define PS_TEXTUREMODES_DOT_ZW(ts) CalcDot(ts); n = Normal2(ts); if (n.y==0) v=1;else v = n.x / n.y; t[ts] = v // TODO : Make depth-check use result of division, but how?
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3vspec */ #define PS_TEXTUREMODES_DOT_RFLCT_SPEC(ts) CalcDot(ts); n = Normal3(ts); s = Reflect(n, Eye); v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_3D(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample3D(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_CUBE(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
@ -323,6 +337,34 @@ float3 DoBumpEnv(const float4 TexCoord, const float4 BumpEnvMat, const float4 sr
PS_OUTPUT main(const PS_INPUT xIn)
{
// fogging
const float fogDepth = xIn.iFog.x; // Don't abs this value! Test-case : DolphinClassic xdk sampl
const int fogTableMode = FOGINFO.x;
const float fogDensity = FOGINFO.y;
const float fogStart = FOGINFO.z;
const float fogEnd = FOGINFO.w;
const int FOG_TABLE_NONE = 0;
const int FOG_TABLE_EXP = 1;
const int FOG_TABLE_EXP2 = 2;
const int FOG_TABLE_LINEAR = 3;
float fogFactor;
if(FOGENABLE == 0){
fogFactor = 1;
}
else{
if(fogTableMode == FOG_TABLE_NONE)
fogFactor = fogDepth;
if(fogTableMode == FOG_TABLE_EXP)
fogFactor = 1 / exp(fogDepth * fogDensity); // 1 / e^(d * density)
if(fogTableMode == FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); // 1 / e^((d * density)^2)
if(fogTableMode == FOG_TABLE_LINEAR)
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
}
// Local constants
const float4 zero = 0;
const float4 half = 0.5; // = s_negbias(zero)
@ -356,12 +398,11 @@ PS_OUTPUT main(const PS_INPUT xIn)
// Note : VFACE/FrontFace has been unreliable, investigate again if some test-case shows bland colors
v0 = isFrontFace ? xIn.iD0 : xIn.iB0; // Diffuse front/back
v1 = isFrontFace ? xIn.iD1 : xIn.iB1; // Specular front/back
fog = float4(c_fog.rgb, xIn.iFog); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
fog = float4(c_fog.rgb, saturate(fogFactor)); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
// Xbox shader program
)DELIMITER", /* This terminates the 2nd raw string within the 16380 single-byte characters limit. // */
// Third and last raw string, the footer :
R"DELIMITER(
// Xbox shader program will be inserted here
// <XBOX SHADER PROGRAM GOES HERE>
// End Xbox shader program
// Copy r0.rgba to output
PS_OUTPUT xOut;
@ -370,5 +411,3 @@ R"DELIMITER(
return xOut;
}
// End of pixel shader footer)DELIMITER" /* This terminates the footer raw string" // */

View file

@ -0,0 +1,108 @@
// Xbox HLSL pretransformed vertex shader
// Default values for vertex registers, and whether to use them
uniform float4 vRegisterDefaultValues[16] : register(c192);
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
uniform float4 xboxScreenspaceScale : register(c212);
uniform float4 xboxScreenspaceOffset : register(c213);
uniform float4 xboxTextureScale[4] : register(c214);
// Parameters for mapping the shader's fog output value to a fog factor
uniform float4 CxbxFogInfo: register(c218); // = CXBX_D3DVS_CONSTREG_FOGINFO
struct VS_INPUT
{
float4 v[16] : TEXCOORD;
};
// Output registers
struct VS_OUTPUT
{
float4 oPos : POSITION; // Homogeneous clip space position
float4 oD0 : COLOR0; // Primary color (front-facing)
float4 oD1 : COLOR1; // Secondary color (front-facing)
float oFog : FOG; // Fog coordinate
float oPts : PSIZE; // Point size
float4 oB0 : TEXCOORD4; // Back-facing primary color
float4 oB1 : TEXCOORD5; // Back-facing secondary color
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
};
float4 reverseScreenspaceTransform(float4 oPos)
{
// Scale screenspace coordinates (0 to viewport width/height) to -1 to +1 range
// On Xbox, oPos should contain the vertex position in screenspace
// We need to reverse this transformation
// Conventionally, each Xbox Vertex Shader includes instructions like this
// mul oPos.xyz, r12, c-38
// +rcc r1.x, r12.w
// mad oPos.xyz, r12, r1.x, c-37
// where c-37 and c-38 are reserved transform values
// Reverse screenspace offset
oPos -= xboxScreenspaceOffset;
// Reverse screenspace scale
oPos /= xboxScreenspaceScale;
// Ensure w is nonzero
if(oPos.w == 0) oPos.w = 1;
// Reverse perspective divide
oPos.xyz *= oPos.w;
return oPos;
}
VS_OUTPUT main(const VS_INPUT xIn)
{
// Input registers
float4 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
// Unpack 16 flags from 4 float4 constant registers
float vRegisterDefaultFlags[16] = (float[16])vRegisterDefaultFlagsPacked;
// Initialize input registers from the vertex buffer data
// Or use the register's default value (which can be changed by the title)
#define init_v(i) v##i = lerp(xIn.v[i], vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
// Note : unroll manually instead of for-loop, because of the ## concatenation
init_v( 0); init_v( 1); init_v( 2); init_v( 3);
init_v( 4); init_v( 5); init_v( 6); init_v( 7);
init_v( 8); init_v( 9); init_v(10); init_v(11);
init_v(12); init_v(13); init_v(14); init_v(15);
// For passthrough, map output variables to their corresponding input registers
float4 oPos = v0;
float4 oD0 = v3;
float4 oD1 = v4;
float4 oFog = v5;
float4 oPts = v6;
float4 oB0 = v7;
float4 oB1 = v8;
float4 oT0 = v9;
float4 oT1 = v10;
float4 oT2 = v11;
float4 oT3 = v12;
// Copy variables to output struct
VS_OUTPUT xOut;
xOut.oPos = reverseScreenspaceTransform(oPos);
xOut.oD0 = saturate(oD0);
xOut.oD1 = saturate(oD1);
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
// Scale textures (TODO: or should we apply this to the input register values?)
xOut.oT0 = oT0 / xboxTextureScale[0];
xOut.oT1 = oT1 / xboxTextureScale[1];
xOut.oT2 = oT2 / xboxTextureScale[2];
xOut.oT3 = oT3 / xboxTextureScale[3];
return xOut;
}

View file

@ -1,6 +1,3 @@
// This starts the raw string (comment to get syntax highlighting, UNCOMMENT to compile) :
R"DELIMITER(// Xbox HLSL vertex shader (template populated at runtime)
struct VS_INPUT
{
float4 v[16] : TEXCOORD;
@ -323,40 +320,20 @@ VS_OUTPUT main(const VS_INPUT xIn)
init_v( 8); init_v( 9); init_v(10); init_v(11);
init_v(12); init_v(13); init_v(14); init_v(15);
// Xbox shader program)DELIMITER", /* This terminates the header raw string" // */
// Temp variable for paired VS instruction
float4 temp;
R"DELIMITER(
// Xbox shader program will be inserted here
// <XBOX SHADER PROGRAM GOES HERE>
// End Xbox shader program
// Copy variables to output struct
VS_OUTPUT xOut;
// Fogging
// TODO deduplicate
const float fogDepth = oFog.x; // Don't abs this value! Test-case : DolphinClassic xdk sample
const float fogTableMode = CxbxFogInfo.x;
const float fogDensity = CxbxFogInfo.y;
const float fogStart = CxbxFogInfo.z;
const float fogEnd = CxbxFogInfo.w;
const float FOG_TABLE_NONE = 0;
const float FOG_TABLE_EXP = 1;
const float FOG_TABLE_EXP2 = 2;
const float FOG_TABLE_LINEAR = 3;
float fogFactor;
if(fogTableMode == FOG_TABLE_NONE)
fogFactor = fogDepth;
if(fogTableMode == FOG_TABLE_EXP)
fogFactor = 1 / exp(fogDepth * fogDensity); /* / 1 / e^(d * density)*/
if(fogTableMode == FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); /* / 1 / e^((d * density)^2)*/
if(fogTableMode == FOG_TABLE_LINEAR)
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
xOut.oPos = reverseScreenspaceTransform(oPos);
xOut.oD0 = saturate(oD0);
xOut.oD1 = saturate(oD1);
xOut.oFog = fogFactor; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
@ -368,5 +345,3 @@ R"DELIMITER(
return xOut;
}
// End of vertex shader footer)DELIMITER" /* This terminates the footer raw string" // */

File diff suppressed because it is too large Load diff

View file

@ -4256,3 +4256,27 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderInput)
return 0;
}
// ******************************************************************
// * patch: D3DDevice_BlockUntilVerticalBlank
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank)()
{
LOG_FUNC();
std::unique_lock<std::mutex> lk(g_VBConditionMutex);
g_VBConditionVariable.wait(lk);
}
// ******************************************************************
// * patch: D3DDevice_SetVerticalBlankCallback
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback)
(
X_D3DVBLANKCALLBACK pCallback
)
{
LOG_FUNC_ONE_ARG(pCallback);
g_pXbox_VerticalBlankCallback = pCallback;
}

View file

@ -40,26 +40,7 @@
void LookupTrampolinesD3D();
// initialize render window
extern void CxbxInitWindow(bool bFullInit);
extern void CxbxSetPixelContainerHeader
(
xbox::X_D3DPixelContainer* pPixelContainer,
DWORD Common,
UINT Width,
UINT Height,
UINT Levels,
xbox::X_D3DFORMAT Format,
UINT Dimensions,
UINT Pitch
);
extern uint8_t *ConvertD3DTextureToARGB(
xbox::X_D3DPixelContainer *pXboxPixelContainer,
uint8_t *pSrc,
int *pWidth, int *pHeight,
int TextureStage = 0
);
extern void CxbxInitWindow();
void CxbxUpdateNativeD3DResources();
@ -163,14 +144,14 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetDisplayFieldStatus)
);
// ******************************************************************
// * patch: D3DDevice_BeginPush
// * patch: D3DDevice_BeginPush_4
// ******************************************************************
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush)(dword_xt Count);
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush_4)(dword_xt Count);
// ******************************************************************
// * patch: D3DDevice_BeginPush2 //two arg version for xdk before 4531
// * patch: D3DDevice_BeginPush_8
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush2)(dword_xt Count, dword_xt **ppPush);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush_8)(dword_xt Count, dword_xt **ppPush);
// ******************************************************************
// * patch: D3DDevice_EndPush
@ -232,8 +213,8 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader)
dword_xt Address
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1)
(
dword_xt Address
);
@ -321,6 +302,8 @@ X_D3DSurface* WINAPI EMUPATCH(D3DDevice_GetBackBuffer2)
int_xt BackBuffer
);
X_D3DSurface* WINAPI EMUPATCH(D3DDevice_GetBackBuffer2_0__LTCG_eax1)();
// ******************************************************************
// * patch: D3DDevice_GetBackBuffer
// ******************************************************************
@ -366,7 +349,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetShaderConstantMode)
X_VERTEXSHADERCONSTANTMODE Mode
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetShaderConstantMode_0)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetShaderConstantMode_0__LTCG_eax1)();
// ******************************************************************
// * patch: D3DDevice_Reset
@ -376,6 +359,10 @@ xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset)
X_D3DPRESENT_PARAMETERS *pPresentationParameters
);
xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset_0__LTCG_edi1)();
xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset_0__LTCG_ebx1)();
// ******************************************************************
// * patch: D3DDevice_GetRenderTarget
// ******************************************************************
@ -522,7 +509,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader)
dword_xt Handle
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle)();
// ******************************************************************
// * patch: D3DDevice_CreateTexture2
@ -628,7 +615,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture)
dword_xt Stage
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage)
(
X_D3DBaseTexture *pTexture
);
@ -1310,7 +1297,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform)
CONST D3DMATRIX *pMatrix
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform_0)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform_0__LTCG_eax1_edx2)();
// ******************************************************************
// * patch: D3DDevice_MultiplyTransform
@ -1412,13 +1399,22 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices)
);
// ******************************************************************
// * patch: D3DDevice_DrawVertices_4
// * patch: D3DDevice_DrawVertices_4__LTCG_ecx2_eax3
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3)
(
X_D3DPRIMITIVETYPE PrimitiveType
);
// ******************************************************************
// * patch: D3DDevice_DrawVertices_8__LTCG_eax3
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_8__LTCG_eax3)
(
X_D3DPRIMITIVETYPE PrimitiveType,
uint_xt StartVertex
);
// ******************************************************************
// * patch: D3DDevice_DrawVerticesUP
// ******************************************************************
@ -1430,7 +1426,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP)
uint_xt VertexStreamZeroStride
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3)
(
X_D3DPRIMITIVETYPE PrimitiveType,
uint_xt VertexCount,
@ -1912,14 +1908,17 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetTexture)
);
// ******************************************************************
// * patch: D3DDevice_SetStateVB (D3D::CDevice::SetStateVB)
// * patch: CDevice_SetStateVB (D3D::CDevice::SetStateVB)
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStateVB)( xbox::ulong_xt Unknown1 );
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB)(xbox::ulong_xt Unknown1);
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB_8)(xbox::addr_xt _this, xbox::ulong_xt Unknown1);
// ******************************************************************
// * patch: D3DDevice_SetStateUP (D3D::CDevice::SetStateUP)
// * patch: CDevice_SetStateUP (D3D::CDevice::SetStateUP)
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStateUP)();
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP)();
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP_4)(xbox::addr_xt _this);
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP_0__LTCG_esi1)();
// ******************************************************************
// * patch: D3DDevice_SetStipple

View file

@ -237,6 +237,24 @@ TextureArgs ExecuteTextureStage(
float4 main(const PS_INPUT input) : COLOR {
// Calculate the fog factor
float fogFactor;
if (state.FogEnable == 0){
fogFactor = 1;
}
else{
if (state.FogTableMode == FOG_TABLE_NONE)
fogFactor = input.iFog;
if (state.FogTableMode == FOG_TABLE_EXP)
fogFactor = 1 / exp(input.iFog * state.FogDensity); // 1 / e^(d * density)
if (state.FogTableMode == FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(input.iFog * state.FogDensity, 2)); // 1 / e^((d * density)^2)
if (state.FogTableMode == FOG_TABLE_LINEAR)
fogFactor = (state.FogEnd - input.iFog) / (state.FogEnd - state.FogStart); // (end - d) / (end - start)
if (state.FogEnable == 0)
fogFactor = 1;
}
TexCoords = input.iT;
// Each stage is passed and returns
@ -284,7 +302,7 @@ float4 main(const PS_INPUT input) : COLOR {
// Add fog if enabled
if (state.FogEnable) {
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(input.iFog));
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(fogFactor));
}
// Add specular if enabled

View file

@ -68,6 +68,12 @@ namespace FixedFunctionPixelShader {
const int SAMPLE_3D = 2;
const int SAMPLE_CUBE = 3;
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas
const float FOG_TABLE_NONE = 0;
const float FOG_TABLE_EXP = 1;
const float FOG_TABLE_EXP2 = 2;
const float FOG_TABLE_LINEAR = 3;
// This state is passed to the shader
struct PsTextureStageState {
// Values correspond to XD3D8 version of D3DTEXTURESTAGESTATETYPE
@ -125,6 +131,10 @@ namespace FixedFunctionPixelShader {
alignas(16) float SpecularEnable;
alignas(16) float FogEnable;
alignas(16) float3 FogColor;
alignas(16) float FogTableMode;
alignas(16) float FogDensity;
alignas(16) float FogStart;
alignas(16) float FogEnd;
};
#ifdef __cplusplus
} // FixedFunctionPixelShader namespace

View file

@ -289,19 +289,7 @@ float DoFog(const VS_INPUT xIn)
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W)
fogDepth = Projection.Position.w;
// Calculate the fog factor
// Some of this might be better done in the pixel shader?
float fogFactor;
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_NONE)
fogFactor = fogDepth;
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_EXP)
fogFactor = 1 / exp(fogDepth * state.Fog.Density); // 1 / e^(d * density)
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(fogDepth * state.Fog.Density, 2)); // 1 / e^((d * density)^2)
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_LINEAR)
fogFactor = (state.Fog.End - fogDepth) / (state.Fog.End - state.Fog.Start); // (end - d) / (end - start)
return fogFactor;
return fogDepth;
}
float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
@ -351,9 +339,9 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
// TODO move alongside the texture transformation when it stops angering the HLSL compiler
const float componentCount = state.TexCoordComponentCount[texCoordIndex];
if (componentCount == 1)
texCoord.yzw = float3(1, 0, 0);
texCoord.yzw = float3(0, 0, 1);
if (componentCount == 2)
texCoord.zw = float2(1, 0);
texCoord.zw = float2(0, 1);
if (componentCount == 3)
texCoord.w = 1;
} // Generate texture coordinates
@ -390,14 +378,15 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
// Test case: ProjectedTexture sample, which uses 3 coordinates
// We'll need to implement the divide when D3D stops handling it for us?
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtexturetransformflags
// padding makes no differences in LLE. LLE works with ProjectedTexture sample without padding, but to be devided by w is necessary.
if (projected)
{
if (countFlag == 1)
texCoord.yzw = texCoord.x;
if (countFlag == 2)
texCoord.zw = texCoord.y;
if (countFlag == 3)
texCoord.w = texCoord.z;
//if (countFlag == 1)
//texCoord.yz = texCoord.x;
//if (countFlag == 2)
//texCoord.z = texCoord.y;
//texCoord.xyzw = texCoord.xyzw / texCoord.w;
}
return texCoord;

View file

@ -289,12 +289,7 @@ bool IsTextureSampled(DecodedRegisterCombiner* pShader, int reg)
void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
{
// Include HLSL header and footer as raw strings :
static const std::string hlsl_template[4] = {
#include "core\hle\D3D8\Direct3D9\CxbxPixelShaderTemplate.hlsl"
};
hlsl << hlsl_template[0]; // Start with the HLSL template header
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[0]; // Start with the HLSL template header
hlsl << "\n#define ALPHAKILL {"
<< (pShader->AlphaKill[0] ? "true, " : "false, ")
@ -341,9 +336,9 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
OutputDefineFlag(hlsl, pShader->FinalCombiner.ComplementV1, "PS_FINALCOMBINERSETTING_COMPLEMENT_V1");
OutputDefineFlag(hlsl, pShader->FinalCombiner.ComplementR0, "PS_FINALCOMBINERSETTING_COMPLEMENT_R0");
OutputDefineFlag(hlsl, pShader->FinalCombiner.ClampSum, "PS_FINALCOMBINERSETTING_CLAMP_SUM");
hlsl << '\n';
hlsl << hlsl_template[1];
hlsl << hlsl_template[2];
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[1];
// Generate all four texture stages
for (unsigned i = 0; i < PSH_XBOX_MAX_T_REGISTER_COUNT; i++) {
@ -390,7 +385,7 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
FinalCombinerStageHlsl(hlsl, pShader->FinalCombiner, pShader->hasFinalCombiner);
hlsl << hlsl_template[3]; // Finish with the HLSL template footer
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[2]; // Finish with the HLSL template footer
}
// recompile xbox pixel shader function

View file

@ -36,20 +36,14 @@ void SetXboxMultiSampleType(xbox::X_D3DMULTISAMPLE_TYPE value);
bool XboxRenderStateConverter::Init()
{
if (g_SymbolAddresses.find("D3DDeferredRenderState") != g_SymbolAddresses.end()) {
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3DDeferredRenderState"];
// Get render state
if (g_SymbolAddresses.find("D3D_g_RenderState") != g_SymbolAddresses.end()) {
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3D_g_RenderState"];
} else {
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_RenderState was not found!");
return false;
}
// At this point, D3D__RenderState points to the first Deferred render state
// Do a little magic to verify that it's correct, then count back to determine the
// start offset of the entire structure
VerifyAndFixDeferredRenderStateOffset();
// Now use the verified Deferred offset to derive the D3D__RenderState offset
DeriveRenderStateOffsetFromDeferredRenderStateOffset();
// Build a mapping of Cxbx Render State indexes to indexes within the current XDK
BuildRenderStateMappingTable();
@ -70,54 +64,6 @@ bool IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo& aRenderStateInf
return bIsRenderStateAvailable;
}
void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset()
{
DWORD CullModeOffset = g_SymbolAddresses["D3DRS_CULLMODE"];
// If we found a valid CullMode offset, verify the symbol location
if (CullModeOffset == 0) {
EmuLog(LOG_LEVEL::WARNING, "D3DRS_CULLMODE could not be found. Please update the XbSymbolDatabase submodule");
return;
}
// Calculate index of D3DRS_CULLMODE for this XDK. We start counting from the first deferred state (D3DRS_FOGENABLE)
DWORD CullModeIndex = 0;
for (int i = xbox::X_D3DRS_DEFERRED_FIRST; i < xbox::X_D3DRS_CULLMODE; i++) {
auto RenderStateInfo = GetDxbxRenderStateInfo(i);
if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) {
CullModeIndex++;
}
}
// If the offset was incorrect, calculate the correct offset, log it, and fix it
if ((DWORD)(&D3D__RenderState[CullModeIndex]) != CullModeOffset) {
DWORD CorrectOffset = CullModeOffset - (CullModeIndex * sizeof(DWORD));
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState returned by XboxSymbolDatabase (0x%08X) was incorrect. Correcting to be 0x%08X.\nPlease file an issue with the XbSymbolDatabase project", D3D__RenderState, CorrectOffset);
D3D__RenderState = (uint32_t*)CorrectOffset;
}
}
void XboxRenderStateConverter::DeriveRenderStateOffsetFromDeferredRenderStateOffset()
{
// When this function is called. D3D__RenderState actually points to the first deferred render state
// (this is X_D3DRS_FOGENABLE). We can count back from this using our RenderStateInfo table to find
// the start of D3D__RenderStates.
// Count the number of render states (for this XDK) between 0 and the first deferred render state (D3DRS_FOGENABLE)
int FirstDeferredRenderStateOffset = 0;
for (unsigned int RenderState = xbox::X_D3DRS_FIRST; RenderState < xbox::X_D3DRS_DEFERRED_FIRST; RenderState++) {
// if the current renderstate exists in this XDK version, count it
auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState);
if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) {
FirstDeferredRenderStateOffset++;
}
}
// At this point, FirstDeferredRenderStateOffset should point to the index of D3DRS_FOGENABLE for the given XDK
// This will be correct as long as our table DxbxRenderStateInfo is correct
// We can get the correct 0 offset by using a negative index
D3D__RenderState = &D3D__RenderState[-FirstDeferredRenderStateOffset];
}
void XboxRenderStateConverter::BuildRenderStateMappingTable()
{
EmuLog(LOG_LEVEL::INFO, "Building Cxbx to XDK Render State Mapping Table");

View file

@ -29,10 +29,18 @@
#include <d3dcompiler.h>
#include "Shader.h"
#include "common/FilePaths.hpp" // For szFilePath_CxbxReloaded_Exe
#include "core\kernel\init\CxbxKrnl.h" // LOG_TEST_CASE
#include "core\kernel\support\Emu.h" // EmuLog
#include <filesystem>
#include <fstream>
#include <array>
#include <thread>
//#include <sstream>
ShaderSources g_ShaderSources;
std::string DebugPrependLineNumbers(std::string shaderString) {
std::stringstream shader(shaderString);
auto debugShader = std::stringstream();
@ -140,3 +148,173 @@ extern HRESULT EmuCompileShader
return hRet;
}
std::ifstream OpenWithRetry(const std::string& path) {
auto fstream = std::ifstream(path);
int failures = 0;
while (fstream.fail()) {
Sleep(50);
fstream = std::ifstream(path);
if (failures++ > 10) {
// crash?
CxbxrAbort("Error opening shader file: %s", path);
break;
}
}
return fstream;
}
int ShaderSources::Update() {
int versionOnDisk = shaderVersionOnDisk;
if (shaderVersionLoadedFromDisk != versionOnDisk) {
LoadShadersFromDisk();
shaderVersionLoadedFromDisk = versionOnDisk;
}
return shaderVersionLoadedFromDisk;
}
void ShaderSources::LoadShadersFromDisk() {
const auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
.parent_path()
.append("hlsl");
// Pixel Shader Template
{
std::stringstream tmp;
auto dir = hlslDir;
dir.append("CxbxPixelShaderTemplate.hlsl");
tmp << OpenWithRetry(dir.string()).rdbuf();
std::string hlsl = tmp.str();
// Split the HLSL file on insertion points
std::array<std::string, 2> insertionPoints = {
"// <HARDCODED STATE GOES HERE>\n",
"// <XBOX SHADER PROGRAM GOES HERE>\n",
};
int pos = 0;
for (int i = 0; i < insertionPoints.size(); i++) {
auto insertionPoint = insertionPoints[i];
auto index = hlsl.find(insertionPoint, pos);
if (index == std::string::npos) {
// Handle broken shaders
this->pixelShaderTemplateHlsl[i] = "";
}
else {
this->pixelShaderTemplateHlsl[i] = hlsl.substr(pos, index - pos);
pos = index + insertionPoint.length();
}
}
this->pixelShaderTemplateHlsl[insertionPoints.size()] = hlsl.substr(pos);
}
// Fixed Function Pixel Shader
{
auto dir = hlslDir;
this->fixedFunctionPixelShaderPath = dir.append("FixedFunctionPixelShader.hlsl").string();
std::stringstream tmp;
tmp << OpenWithRetry(this->fixedFunctionPixelShaderPath).rdbuf();
this->fixedFunctionPixelShaderHlsl = tmp.str();
}
// Vertex Shader Template
{
std::stringstream tmp;
auto dir = hlslDir;
dir.append("CxbxVertexShaderTemplate.hlsl");
tmp << OpenWithRetry(dir.string()).rdbuf();
std::string hlsl = tmp.str();
const std::string insertionPoint = "// <XBOX SHADER PROGRAM GOES HERE>\n";
auto index = hlsl.find(insertionPoint);
if (index == std::string::npos) {
// Handle broken shaders
this->vertexShaderTemplateHlsl[0] = hlsl;
this->vertexShaderTemplateHlsl[1] = "";
}
else
{
this->vertexShaderTemplateHlsl[0] = hlsl.substr(0, index);
this->vertexShaderTemplateHlsl[1] = hlsl.substr(index + insertionPoint.length());
}
}
// Fixed Function Vertex Shader
{
auto dir = hlslDir;
this->fixedFunctionVertexShaderPath = dir.append("FixedFunctionVertexShader.hlsl").string();
std::stringstream tmp;
tmp << OpenWithRetry(this->fixedFunctionVertexShaderPath).rdbuf();
this->fixedFunctionVertexShaderHlsl = tmp.str();
}
// Passthrough Vertex Shader
{
auto dir = hlslDir;
this->vertexShaderPassthroughPath = dir.append("CxbxVertexShaderPassthrough.hlsl").string();
std::stringstream tmp;
tmp << OpenWithRetry(this->vertexShaderPassthroughPath).rdbuf();
this->vertexShaderPassthroughHlsl = tmp.str();
}
}
void ShaderSources::InitShaderHotloading() {
static std::jthread fsWatcherThread;
if (fsWatcherThread.joinable()) {
EmuLog(LOG_LEVEL::ERROR2, "Ignoring request to start shader file watcher - it has already been started.");
return;
}
EmuLog(LOG_LEVEL::DEBUG, "Starting shader file watcher...");
fsWatcherThread = std::jthread([]{
// Determine the filename and directory for the fixed function shader
char cxbxExePath[MAX_PATH];
GetModuleFileName(GetModuleHandle(nullptr), cxbxExePath, MAX_PATH);
auto hlslDir = std::filesystem::path(cxbxExePath).parent_path().append("hlsl/");
HANDLE changeHandle = FindFirstChangeNotification(hlslDir.string().c_str(), false, FILE_NOTIFY_CHANGE_LAST_WRITE);
if (changeHandle == INVALID_HANDLE_VALUE) {
DWORD errorCode = GetLastError();
EmuLog(LOG_LEVEL::ERROR2, "Error initializing shader file watcher: %d", errorCode);
return 1;
}
while (true) {
if (FindNextChangeNotification(changeHandle)) {
WaitForSingleObject(changeHandle, INFINITE);
// Wait for changes to stop..
// Will usually be at least two - one for the file and one for the directory
while (true) {
FindNextChangeNotification(changeHandle);
if (WaitForSingleObject(changeHandle, 100) == WAIT_TIMEOUT) {
break;
}
}
EmuLog(LOG_LEVEL::DEBUG, "Change detected in shader folder");
g_ShaderSources.shaderVersionOnDisk++;
}
else {
EmuLog(LOG_LEVEL::ERROR2, "Shader filewatcher failed to get the next notification");
break;
}
}
EmuLog(LOG_LEVEL::DEBUG, "Shader file watcher exiting...");
// until there is a way to disable hotloading
// this is always an error
FindCloseChangeNotification(changeHandle);
return 1;
});
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <atomic>
#include <string> // std::string
#include <d3dcompiler.h> // ID3DBlob (via d3d9.h > d3d11shader.h > d3dcommon.h)
@ -10,3 +11,38 @@ extern HRESULT EmuCompileShader
ID3DBlob** ppHostShader,
const char* pSourceName = nullptr
);
struct ShaderSources {
// Pixel Shader
std::string pixelShaderTemplateHlsl[3];
std::string fixedFunctionPixelShaderHlsl;
std::string fixedFunctionPixelShaderPath;
// Vertex Shader
std::string vertexShaderTemplateHlsl[2];
std::string fixedFunctionVertexShaderHlsl;
std::string fixedFunctionVertexShaderPath;
std::string vertexShaderPassthroughHlsl;
std::string vertexShaderPassthroughPath;
// Load shaders from disk (if out-of-date)
// and return the current loaded shader version
int Update();
// Start a thread to watch for changes in the shader folder
void InitShaderHotloading();
private:
void LoadShadersFromDisk();
// counts upwards on every change detected to the shader source files at runtime
std::atomic_int shaderVersionOnDisk = 0;
// current loaded shader version
// Initialized to < shaderVersionOnDisk
int shaderVersionLoadedFromDisk = -1;
};
extern ShaderSources g_ShaderSources;

View file

@ -38,7 +38,7 @@
#include <optional>
typedef struct {
char* S; // String representation.
const char* S; // String representation.
bool IsSamplerState; // True if the state maps to a Sampler State instead of Texture Stage
DWORD PC; // PC Index
} TextureStateInfo;
@ -79,11 +79,12 @@ TextureStateInfo CxbxTextureStateInfo[] = {
bool XboxTextureStateConverter::Init(XboxRenderStateConverter* pState)
{
// Deferred states start at 0, this means that D3DDeferredTextureState IS D3D__TextureState
// Deferred states start at 0, this means that D3D_g_DeferredTextureState IS D3D__TextureState
// No further works is required to derive the offset
if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) {
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3DDeferredTextureState"];
if (g_SymbolAddresses.find("D3D_g_DeferredTextureState") != g_SymbolAddresses.end()) {
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3D_g_DeferredTextureState"];
} else {
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_DeferredTextureState was not found!");
return false;
}
@ -361,9 +362,9 @@ DWORD NormalizeValue(DWORD xboxState, DWORD value) {
uint32_t XboxTextureStateConverter::Get(int textureStage, DWORD xboxState) {
if (textureStage < 0 || textureStage > 3)
CxbxKrnlCleanup("Requested texture stage was out of range: %d", textureStage);
CxbxrAbort("Requested texture stage was out of range: %d", textureStage);
if (xboxState < xbox::X_D3DTSS_FIRST || xboxState > xbox::X_D3DTSS_LAST)
CxbxKrnlCleanup("Requested texture state was out of range: %d", xboxState);
CxbxrAbort("Requested texture state was out of range: %d", xboxState);
// Read the value of the current stage/state from the Xbox data structure
DWORD rawValue = D3D__TextureState[(textureStage * xbox::X_D3DTS_STAGESIZE) + XboxTextureStateOffsets[xboxState]];

View file

@ -5,14 +5,11 @@
#include "core\kernel\init\CxbxKrnl.h" // implicit CxbxKrnl_Xbe used in LOG_TEST_CASE
#include "core\kernel\support\Emu.h" // LOG_TEST_CASE (via Logging.h)
#include <fstream>
#include <sstream> // std::stringstream
extern const char* g_vs_model = vs_model_3_0;
// HLSL generation
void OutputHlsl(std::stringstream& hlsl, VSH_IMD_OUTPUT& dest)
{
void DestRegisterHlsl(std::stringstream& hlsl, VSH_IMD_DEST& dest) {
static const char* OReg_Name[/*VSH_OREG_NAME*/] = {
"oPos",
"???",
@ -33,34 +30,37 @@ void OutputHlsl(std::stringstream& hlsl, VSH_IMD_OUTPUT& dest)
};
switch (dest.Type) {
case IMD_OUTPUT_C:
case IMD_DEST_C:
// Access the HLSL capital C[] constants array, with the index bias applied :
// TODO : Avoid out-of-bound writes (perhaps writing to a reserved index?)
hlsl << "C[" << dest.Address + X_D3DSCM_CORRECTION << "]";
LOG_TEST_CASE("Vertex shader writes to constant table");
break;
case IMD_OUTPUT_R:
case IMD_DEST_R:
hlsl << "r" << dest.Address;
break;
case IMD_OUTPUT_O:
case IMD_DEST_O:
assert(dest.Address < OREG_A0X);
hlsl << OReg_Name[dest.Address];
break;
case IMD_OUTPUT_A0X:
case IMD_DEST_A0X:
hlsl << "a0";
break;
default:
assert(false);
break;
}
}
void DestMaskHlsl(std::stringstream& hlsl, VSH_IMD_DEST& dest)
{
// Write the mask as a separate argument to the opcode defines
// (No space, so that "dest,mask, ..." looks close to "dest.mask, ...")
hlsl << ",";
// Detect oFog masks other than x
// Test case: Lego Star Wars II (menu)
if (dest.Type == IMD_OUTPUT_O &&
if (dest.Type == IMD_DEST_O &&
dest.Address == OREG_OFOG &&
dest.Mask != MASK_X)
{
@ -77,9 +77,9 @@ void OutputHlsl(std::stringstream& hlsl, VSH_IMD_OUTPUT& dest)
if (dest.Mask & MASK_W) hlsl << "w";
}
void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool IndexesWithA0_X)
void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool IndexesWithA0_X, bool useTemp)
{
static char* RegisterName[/*VSH_PARAMETER_TYPE*/] = {
static const char* RegisterName[/*VSH_PARAMETER_TYPE*/] = {
"?", // PARAM_UNKNOWN = 0,
"r", // PARAM_R, // Temporary (scRatch) registers
"v", // PARAM_V, // Vertex registers
@ -91,7 +91,10 @@ void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool Index
hlsl << "-";
}
if (param.ParameterType == PARAM_C) {
if (useTemp) {
hlsl << "temp";
}
else if (param.Type == PARAM_C) {
// Access constant registers through our HLSL c() function,
// which allows dumping negative indices (like Xbox shaders),
// and which returns zero when out-of-bounds indices are passed in:
@ -111,7 +114,7 @@ void ParameterHlsl(std::stringstream& hlsl, VSH_IMD_PARAMETER& param, bool Index
}
}
else {
hlsl << RegisterName[param.ParameterType] << param.Address;
hlsl << RegisterName[param.Type] << param.Address;
}
// Write the swizzle if we need to
@ -174,25 +177,107 @@ void BuildShader(IntermediateVertexShader* pShader, std::stringstream& hlsl)
/*ILU_LIT:*/"x_lit" // = 7 - all values of the 3 bits are used
};
for (size_t i = 0; i < pShader->Instructions.size(); i++) {
VSH_INTERMEDIATE_FORMAT& IntermediateInstruction = pShader->Instructions[i];
auto WriteOp = [&](
const std::string& opcode,
VSH_IMD_DEST dest,
int paramCount, VSH_IMD_PARAMETER* params,
bool indexesWithA0_X,
bool iluUseTempParam
) {
// opcode(dest, a, b, c);
hlsl << "\n " << opcode << "(";
std::string str;
if (IntermediateInstruction.MAC > MAC_NOP) {
str = VSH_MAC_HLSL[IntermediateInstruction.MAC];
}
else {
str = VSH_ILU_HLSL[IntermediateInstruction.ILU];
}
DestRegisterHlsl(hlsl, dest);
DestMaskHlsl(hlsl, dest);
hlsl << "\n " << str << "("; // opcode
OutputHlsl(hlsl, IntermediateInstruction.Output);
for (unsigned i = 0; i < IntermediateInstruction.ParamCount; i++) {
for (int i = 0; i < paramCount; i++) {
hlsl << ", ";
ParameterHlsl(hlsl, IntermediateInstruction.Parameters[i], IntermediateInstruction.IndexesWithA0_X);
ParameterHlsl(hlsl, params[i], indexesWithA0_X, iluUseTempParam);
}
hlsl << ");";
};
for (size_t i = 0; i < pShader->Instructions.size(); i++) {
VSH_IMD_INSTR& in = pShader->Instructions[i];
// Paired if both MAC and ILU write to a dest register
bool isPaired =
in.MAC.Opcode != MAC_NOP &&
in.ILU.Opcode != ILU_NOP &&
(in.MAC.Dest.Mask || in.ORegSource == SRC_MAC) &&
(in.ILU.Dest.Mask || in.ORegSource == SRC_ILU);
// If there are two "paired" instructions that need to run "simultaneously",
// we need to prevent the output of the first instruction interfering
// with the input of the second instruction
// If the MAC output is the same as the ILU input
// we will use a temp variable to hold the ILU input
VSH_IMD_DEST* iluTemp = nullptr;
if (isPaired) {
if (in.MAC.Dest.Address == in.ILU.Parameter.Address &&
(in.MAC.Dest.Type == IMD_DEST_C && in.ILU.Parameter.Type == PARAM_C ||
in.MAC.Dest.Type == IMD_DEST_R && in.ILU.Parameter.Type == PARAM_R ||
in.MAC.Dest.Type == IMD_DEST_A0X && in.ILU.Parameter.Type == PARAM_C && in.IndexesWithA0_X)) {
// Normal MAC output matches ILU input
iluTemp = &in.MAC.Dest;
}
else if (in.ORegSource == SRC_MAC &&
in.ORegDest.Type == IMD_DEST_O && in.ORegDest.Address == 0 &&
in.ILU.Parameter.Type == PARAM_R && in.ILU.Parameter.Address == 12) {
// OReg MAC output matches ILU input
// Note oPos is the same as r12
iluTemp = &in.ORegDest;
}
if (iluTemp) {
// MAC and ILU use the same register.
// This is fine unless the ILU op uses a component written to by the MAC op
bool conflict = false;
for (int s = 0; s < 4; s++) {
auto swizzle = in.ILU.Parameter.Swizzle[s];
if (iluTemp->Mask & MASK_X && swizzle == SWIZZLE_X ||
iluTemp->Mask & MASK_Y && swizzle == SWIZZLE_Y ||
iluTemp->Mask & MASK_Z && swizzle == SWIZZLE_Z ||
iluTemp->Mask & MASK_W && swizzle == SWIZZLE_W) {
conflict = true;
break;
}
}
if (!conflict) {
iluTemp = nullptr; // We don't need a temp after all
}
}
}
if (iluTemp) {
// Write the ILU input to a temp
hlsl << "\n " << "temp = ";
DestRegisterHlsl(hlsl, *iluTemp);
hlsl << ";";
}
// Write MAC op
if (in.MAC.Opcode != MAC_NOP) {
if (in.MAC.Dest.Mask) {
WriteOp(VSH_MAC_HLSL[in.MAC.Opcode], in.MAC.Dest, in.MAC.ParamCount, in.MAC.Parameters, in.IndexesWithA0_X, false);
}
if (in.ORegSource == SRC_MAC && in.ORegDest.Mask) {
WriteOp(VSH_MAC_HLSL[in.MAC.Opcode], in.ORegDest, in.MAC.ParamCount, in.MAC.Parameters, in.IndexesWithA0_X, false);
}
}
// Write ILU op
if (in.ILU.Opcode != ILU_NOP) {
if (in.ILU.Dest.Mask) {
WriteOp(VSH_ILU_HLSL[in.ILU.Opcode], in.ILU.Dest, 1, &in.ILU.Parameter, in.IndexesWithA0_X, iluTemp);
}
if (in.ORegSource == SRC_ILU && in.ORegDest.Mask) {
WriteOp(VSH_ILU_HLSL[in.ILU.Opcode], in.ORegDest, 1, &in.ILU.Parameter, in.IndexesWithA0_X, iluTemp);
}
}
hlsl << "\n"; // Group operations by instruction
}
}
@ -203,26 +288,23 @@ extern HRESULT EmuCompileVertexShader
ID3DBlob** ppHostShader
)
{
// Include HLSL header and footer as raw strings :
static std::string hlsl_template[2] = {
#include "core\hle\D3D8\Direct3D9\CxbxVertexShaderTemplate.hlsl"
};
auto hlsl_stream = std::stringstream();
hlsl_stream << hlsl_template[0]; // Start with the HLSL template header
assert(pIntermediateShader->Instructions.size() > 0);
BuildShader(pIntermediateShader, hlsl_stream);
hlsl_stream << hlsl_template[1]; // Finish with the HLSL template footer
// Combine the shader template with the shader program
auto hlsl_stream = std::stringstream();
hlsl_stream << g_ShaderSources.vertexShaderTemplateHlsl[0]; // Start with the HLSL template header
BuildShader(pIntermediateShader, hlsl_stream);
hlsl_stream << g_ShaderSources.vertexShaderTemplateHlsl[1]; // Finish with the HLSL template footer
std::string hlsl_str = hlsl_stream.str();
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
const char* notionalSourceName = "CxbxVertexShaderTemplate.hlsl";
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, notionalSourceName);
if (FAILED(hRet) && (g_vs_model != vs_model_3_0)) {
// If the shader failed in the default vertex shader model, retry in vs_model_3_0
// This allows shaders too large for 2_a to be compiled (Test Case: Shenmue 2)
EmuLog(LOG_LEVEL::WARNING, "Shader compile failed. Retrying with shader model 3.0");
hRet = EmuCompileShader(hlsl_str, vs_model_3_0, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
hRet = EmuCompileShader(hlsl_str, vs_model_3_0, ppHostShader, notionalSourceName);
}
return hRet;
@ -230,174 +312,10 @@ extern HRESULT EmuCompileVertexShader
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader)
{
static ID3DBlob* pShader = nullptr;
// TODO does this need to be thread safe?
if (pShader == nullptr) {
// Determine the filename and directory for the fixed function shader
auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
.parent_path()
.append("hlsl");
auto sourceFile = hlslDir.append("FixedFunctionVertexShader.hlsl").string();
// Load the shader into a string
std::ifstream hlslStream(sourceFile);
std::stringstream hlsl;
hlsl << hlslStream.rdbuf();
// Compile the shader
EmuCompileShader(hlsl.str(), g_vs_model, &pShader, sourceFile.c_str());
}
*ppHostShader = pShader;
EmuCompileShader(g_ShaderSources.fixedFunctionVertexShaderHlsl, g_vs_model, ppHostShader, g_ShaderSources.fixedFunctionVertexShaderPath.c_str());
};
static ID3DBlob* pPassthroughShader = nullptr;
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
{
// TODO does this need to be thread safe?
if (pPassthroughShader == nullptr) {
auto hlsl =
R"(
// Xbox HLSL pretransformed vertex shader
// Default values for vertex registers, and whether to use them
uniform float4 vRegisterDefaultValues[16] : register(c192);
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
uniform float4 xboxScreenspaceScale : register(c212);
uniform float4 xboxScreenspaceOffset : register(c213);
uniform float4 xboxTextureScale[4] : register(c214);
// Parameters for mapping the shader's fog output value to a fog factor
uniform float4 CxbxFogInfo: register(c218); // = CXBX_D3DVS_CONSTREG_FOGINFO
struct VS_INPUT
{
float4 v[16] : TEXCOORD;
};
// Output registers
struct VS_OUTPUT
{
float4 oPos : POSITION; // Homogeneous clip space position
float4 oD0 : COLOR0; // Primary color (front-facing)
float4 oD1 : COLOR1; // Secondary color (front-facing)
float oFog : FOG; // Fog coordinate
float oPts : PSIZE; // Point size
float4 oB0 : TEXCOORD4; // Back-facing primary color
float4 oB1 : TEXCOORD5; // Back-facing secondary color
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
};
float4 reverseScreenspaceTransform(float4 oPos)
{
// Scale screenspace coordinates (0 to viewport width/height) to -1 to +1 range
// On Xbox, oPos should contain the vertex position in screenspace
// We need to reverse this transformation
// Conventionally, each Xbox Vertex Shader includes instructions like this
// mul oPos.xyz, r12, c-38
// +rcc r1.x, r12.w
// mad oPos.xyz, r12, r1.x, c-37
// where c-37 and c-38 are reserved transform values
// Reverse screenspace offset
oPos -= xboxScreenspaceOffset;
// Reverse screenspace scale
oPos /= xboxScreenspaceScale;
// Ensure w is nonzero
if(oPos.w == 0) oPos.w = 1;
// Reverse perspective divide
oPos.xyz *= oPos.w;
return oPos;
}
VS_OUTPUT main(const VS_INPUT xIn)
{
// Input registers
float4 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
// Unpack 16 flags from 4 float4 constant registers
float vRegisterDefaultFlags[16] = (float[16])vRegisterDefaultFlagsPacked;
// Initialize input registers from the vertex buffer data
// Or use the register's default value (which can be changed by the title)
#define init_v(i) v##i = lerp(xIn.v[i], vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
// Note : unroll manually instead of for-loop, because of the ## concatenation
init_v( 0); init_v( 1); init_v( 2); init_v( 3);
init_v( 4); init_v( 5); init_v( 6); init_v( 7);
init_v( 8); init_v( 9); init_v(10); init_v(11);
init_v(12); init_v(13); init_v(14); init_v(15);
// For passthrough, map output variables to their corresponding input registers
float4 oPos = v0;
float4 oD0 = v3;
float4 oD1 = v4;
float4 oFog = v5;
float4 oPts = v6;
float4 oB0 = v7;
float4 oB1 = v8;
float4 oT0 = v9;
float4 oT1 = v10;
float4 oT2 = v11;
float4 oT3 = v12;
// Copy variables to output struct
VS_OUTPUT xOut;
// Fogging
// TODO deduplicate
const float fogDepth = abs(oFog.x);
const float fogTableMode = CxbxFogInfo.x;
const float fogDensity = CxbxFogInfo.y;
const float fogStart = CxbxFogInfo.z;
const float fogEnd = CxbxFogInfo.w;
const float FOG_TABLE_NONE = 0;
const float FOG_TABLE_EXP = 1;
const float FOG_TABLE_EXP2 = 2;
const float FOG_TABLE_LINEAR = 3;
float fogFactor;
if(fogTableMode == FOG_TABLE_NONE)
fogFactor = fogDepth;
if(fogTableMode == FOG_TABLE_EXP)
fogFactor = 1 / exp(fogDepth * fogDensity); /* / 1 / e^(d * density)*/
if(fogTableMode == FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); /* / 1 / e^((d * density)^2)*/
if(fogTableMode == FOG_TABLE_LINEAR)
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
xOut.oPos = reverseScreenspaceTransform(oPos);
xOut.oD0 = saturate(oD0);
xOut.oD1 = saturate(oD1);
xOut.oFog = fogFactor; // Note : Xbox clamps fog in pixel shader
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
// Scale textures (TODO : or should we apply this to the input register values?)
xOut.oT0 = oT0 / xboxTextureScale[0];
xOut.oT1 = oT1 / xboxTextureScale[1];
xOut.oT2 = oT2 / xboxTextureScale[2];
xOut.oT3 = oT3 / xboxTextureScale[3];
return xOut;
}
)";
EmuCompileShader(hlsl, g_vs_model, &pPassthroughShader, "passthrough.hlsl");
}
*ppHostShader = pPassthroughShader;
return 0;
EmuCompileShader(g_ShaderSources.vertexShaderPassthroughHlsl, g_vs_model, ppHostShader, g_ShaderSources.vertexShaderPassthroughPath.c_str());
}

View file

@ -21,5 +21,5 @@ extern HRESULT EmuCompileVertexShader
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader);
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);

View file

@ -1,15 +1,16 @@
#define LOG_PREFIX CXBXR_MODULE::VSHCACHE
#include "VertexShaderSource.h"
#include "VertexShaderCache.h"
#include "core/kernel/init/CxbxKrnl.h"
#include "util/hasher.h"
#include "core/kernel/support/Emu.h"
VertexShaderSource g_VertexShaderSource = VertexShaderSource();
VertexShaderCache g_VertexShaderCache = VertexShaderCache();
// FIXME : This should really be released and created in step with the D3D device lifecycle rather than being a thing on its own
// (And the ResetD3DDevice method should be removed)
ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, ShaderKey key) {
ID3DBlob* pCompiledShader;
@ -25,7 +26,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
// Find a shader
// Return true if the shader was found
bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
bool VertexShaderCache::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
auto it = cache.find(key);
if (it == cache.end()) {
// We didn't find anything! Was CreateShader called?
@ -39,7 +40,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
// Create a new shader
// If the shader was already created, just increase its reference count
ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
ShaderKey VertexShaderCache::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
IntermediateVertexShader intermediateShader;
*pXboxFunctionSize = GetVshFunctionSize(pXboxFunction);
@ -86,7 +87,7 @@ ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction,
}
// Get a shader using the given key
IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
IDirect3DVertexShader* VertexShaderCache::GetShader(ShaderKey key)
{
LazyVertexShader* pLazyShader = nullptr;
@ -113,6 +114,12 @@ IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
EmuLog(LOG_LEVEL::DEBUG, "Waiting for shader %llx...", key);
pCompiledShader = pLazyShader->compileResult.get();
if (!pCompiledShader) {
EmuLog(LOG_LEVEL::ERROR2, "Failed to compile vertex shader for %llx", key);
pLazyShader->isReady = true;
return nullptr;
}
// Create the shader
auto hRet = pD3DDevice->CreateVertexShader
(
@ -145,7 +152,7 @@ IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
}
// Release a shader. Doesn't actually release any resources for now
void VertexShaderSource::ReleaseShader(ShaderKey key)
void VertexShaderCache::ReleaseShader(ShaderKey key)
{
// For now, don't bother releasing any shaders
LazyVertexShader* pLazyShader;
@ -165,8 +172,25 @@ void VertexShaderSource::ReleaseShader(ShaderKey key)
}
}
void VertexShaderSource::ResetD3DDevice(IDirect3DDevice9* newDevice)
void VertexShaderCache::ResetD3DDevice(IDirect3DDevice9* newDevice)
{
EmuLog(LOG_LEVEL::DEBUG, "Resetting D3D device");
cache.clear();
this->pD3DDevice = newDevice;
}
void VertexShaderCache::Clear()
{
for (auto& x : cache) {
if (!x.second.isReady) {
auto pBlob = x.second.compileResult.get();
if (pBlob) {
pBlob->Release();
}
}
else if(x.second.pHostVertexShader) {
x.second.pHostVertexShader->Release();
}
}
cache.clear();
}

View file

@ -8,7 +8,7 @@
typedef uint64_t ShaderKey;
// Manages creation and caching of vertex shaders
class VertexShaderSource {
class VertexShaderCache {
public:
ShaderKey CreateShader(const xbox::dword_xt* pXboxFunction, DWORD* pXboxFunctionSize);
@ -16,6 +16,7 @@ public:
void ReleaseShader(ShaderKey key);
void ResetD3DDevice(IDirect3DDevice9* pD3DDevice);
void Clear();
// TODO
// WriteCacheToDisk
@ -39,9 +40,9 @@ private:
std::mutex cacheMutex;
std::map<ShaderKey, LazyVertexShader> cache;
bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader);
bool _FindShader(ShaderKey key, LazyVertexShader** ppLazyShader);
};
extern VertexShaderSource g_VertexShaderSource;
extern VertexShaderCache g_VertexShaderCache;
#endif

View file

@ -144,6 +144,22 @@ D3DMATRIX* D3D8TransformState::GetWorldView(unsigned i)
return &WorldView[i];
}
void D3D8TransformState::SetWorldView(unsigned i, const D3DMATRIX* pMatrix)
{
assert(i < 4);
if (!pMatrix) {
// null indicates the title is done with setting
// the worldview matrix explicitly
bWorldViewDirty[i] = true;
return;
}
else {
bWorldViewDirty[i] = false;
WorldView[i] = *pMatrix;
}
}
D3DMATRIX* D3D8TransformState::GetWorldViewInverseTranspose(unsigned i)
{
assert(i < 4);

View file

@ -28,6 +28,7 @@ public:
D3D8TransformState();
void SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix);
D3DMATRIX* GetWorldView(unsigned i);
void SetWorldView(unsigned i, const D3DMATRIX* pMatrix);
D3DMATRIX* GetWorldViewInverseTranspose(unsigned i);
// The transforms set by the Xbox title

View file

@ -861,7 +861,7 @@ typedef struct _FormatInfo {
_ComponentEncoding components;
D3DFORMAT pc;
_FormatUsage usage;
char *warning;
const char *warning;
} FormatInfo;
static const FormatInfo FormatInfos[] = {
@ -1047,7 +1047,7 @@ D3DFORMAT EmuXB2PC_D3DFormat(xbox::X_D3DFORMAT Format)
case ((xbox::X_D3DFORMAT)0xffffffff):
return D3DFMT_UNKNOWN; // TODO -oCXBX: Not sure if this counts as swizzled or not...
default:
CxbxKrnlCleanup("EmuXB2PC_D3DFormat: Unknown Format (0x%.08X)", Format);
CxbxrAbort("EmuXB2PC_D3DFormat: Unknown Format (0x%.08X)", Format);
}
return D3DFMT_UNKNOWN;
@ -1129,7 +1129,7 @@ xbox::X_D3DFORMAT EmuPC2XB_D3DFormat(D3DFORMAT Format, bool bPreferLinear)
result = xbox::X_D3DFMT_VERTEXDATA;
break;
default:
CxbxKrnlCleanup("EmuPC2XB_D3DFormat: Unknown Format (%d)", Format);
CxbxrAbort("EmuPC2XB_D3DFormat: Unknown Format (%d)", Format);
}
return result;
@ -1591,6 +1591,220 @@ Direct3D9 states unused :
D3DRS_BLENDOPALPHA = 209 // Blending operation for the alpha channel when D3DRS_SEPARATEDESTALPHAENABLE is TRUE
*/
xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::dword_xt XboxPixelContainer_Format)
{
xbox::X_D3DFORMAT d3d_format = (xbox::X_D3DFORMAT)((XboxPixelContainer_Format & X_D3DFORMAT_FORMAT_MASK) >> X_D3DFORMAT_FORMAT_SHIFT);
return d3d_format;
}
xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::X_D3DPixelContainer* pXboxPixelContainer)
{
// Don't pass in unassigned Xbox pixel container
assert(pXboxPixelContainer != xbox::zeroptr);
return GetXboxPixelContainerFormat(pXboxPixelContainer->Format);
}
void CxbxGetPixelContainerMeasures
(
xbox::X_D3DPixelContainer* pPixelContainer,
// TODO : Add X_D3DCUBEMAP_FACES argument
DWORD dwMipMapLevel, // unused - TODO : Use
UINT* pWidth,
UINT* pHeight,
UINT* pDepth,
UINT* pRowPitch,
// Slice pitch (does not include mipmaps!)
UINT* pSlicePitch
)
{
DWORD Size = pPixelContainer->Size;
xbox::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pPixelContainer);
if (Size != 0)
{
*pDepth = 1;
*pWidth = ((Size & X_D3DSIZE_WIDTH_MASK) /* >> X_D3DSIZE_WIDTH_SHIFT*/) + 1;
*pHeight = ((Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1;
*pRowPitch = (((Size & X_D3DSIZE_PITCH_MASK) >> X_D3DSIZE_PITCH_SHIFT) + 1) * X_D3DTEXTURE_PITCH_ALIGNMENT;
}
else
{
DWORD l2w = (pPixelContainer->Format & X_D3DFORMAT_USIZE_MASK) >> X_D3DFORMAT_USIZE_SHIFT;
DWORD l2h = (pPixelContainer->Format & X_D3DFORMAT_VSIZE_MASK) >> X_D3DFORMAT_VSIZE_SHIFT;
DWORD l2d = (pPixelContainer->Format & X_D3DFORMAT_PSIZE_MASK) >> X_D3DFORMAT_PSIZE_SHIFT;
DWORD dwBPP = EmuXBFormatBitsPerPixel(X_Format);
*pDepth = 1 << l2d;
*pHeight = 1 << l2h;
*pWidth = 1 << l2w;
*pRowPitch = (*pWidth) * dwBPP / 8;
}
*pSlicePitch = (*pRowPitch) * (*pHeight);
if (EmuXBFormatIsCompressed(X_Format)) {
*pRowPitch *= 4;
}
}
bool ConvertD3DTextureToARGBBuffer(
xbox::X_D3DFORMAT X_Format,
uint8_t* pSrc,
int SrcWidth, int SrcHeight, int SrcRowPitch, int SrcSlicePitch,
uint8_t* pDst, int DstRowPitch, int DstSlicePitch,
unsigned int uiDepth ,
xbox::PVOID pPalleteData
)
{
const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format);
if (ConvertRowToARGB == nullptr)
return false; // Unhandled conversion
uint8_t* unswizleBuffer = nullptr;
if (EmuXBFormatIsSwizzled(X_Format)) {
unswizleBuffer = (uint8_t*)malloc(SrcSlicePitch * uiDepth); // TODO : Reuse buffer when performance is important
// First we need to unswizzle the texture data
EmuUnswizzleBox(
pSrc, SrcWidth, SrcHeight, uiDepth,
EmuXBFormatBytesPerPixel(X_Format),
// Note : use src pitch on dest, because this is an intermediate step :
unswizleBuffer, SrcRowPitch, SrcSlicePitch
);
// Convert colors from the unswizzled buffer
pSrc = unswizleBuffer;
}
int AdditionalArgument;
if (X_Format == xbox::X_D3DFMT_P8)
AdditionalArgument = (int)pPalleteData;
else
AdditionalArgument = DstRowPitch;
if (EmuXBFormatIsCompressed(X_Format)) {
if (SrcWidth < 4 || SrcHeight < 4) {
// HACK: The compressed DXT conversion code currently writes more pixels than it should, which can cause a crash.
// This code will get hit when converting compressed texture mipmaps on hardware that somehow doesn't support DXT natively
// (or lied when Cxbx asked it if it does!)
EmuLog(LOG_LEVEL::WARNING, "Converting DXT textures smaller than a block is not currently implemented. Ignoring conversion!");
return true;
}
// All compressed formats (DXT1, DXT3 and DXT5) encode blocks of 4 pixels on 4 lines
SrcHeight = (SrcHeight + 3) / 4;
DstRowPitch *= 4;
}
uint8_t* pSrcSlice = pSrc;
uint8_t* pDstSlice = pDst;
for (unsigned int z = 0; z < uiDepth; z++) {
uint8_t* pSrcRow = pSrcSlice;
uint8_t* pDstRow = pDstSlice;
for (int y = 0; y < SrcHeight; y++) {
*(int*)pDstRow = AdditionalArgument; // Dirty hack, to avoid an extra parameter to all conversion callbacks
ConvertRowToARGB(pSrcRow, pDstRow, SrcWidth);
pSrcRow += SrcRowPitch;
pDstRow += DstRowPitch;
}
pSrcSlice += SrcSlicePitch;
pDstSlice += DstSlicePitch;
}
if (unswizleBuffer)
free(unswizleBuffer);
return true;
}
// Called by WndMain::LoadGameLogo() to load game logo bitmap
uint8_t* ConvertD3DTextureToARGB(
xbox::X_D3DPixelContainer* pXboxPixelContainer,
uint8_t* pSrc,
int* pWidth, int* pHeight,
xbox::PVOID pPalleteData // default = zeroptr
)
{
// Avoid allocating pDest when ConvertD3DTextureToARGBBuffer will fail anyway
xbox::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pXboxPixelContainer);
const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format);
if (ConvertRowToARGB == nullptr)
return nullptr; // Unhandled conversion
unsigned int SrcDepth, SrcRowPitch, SrcSlicePitch;
CxbxGetPixelContainerMeasures(
pXboxPixelContainer,
0, // dwMipMapLevel
(UINT*)pWidth,
(UINT*)pHeight,
&SrcDepth,
&SrcRowPitch,
&SrcSlicePitch
);
// Now we know ConvertD3DTextureToARGBBuffer will do it's thing, allocate the resulting buffer
int DstDepth = 1; // for now TODO : Use SrcDepth when supporting volume textures
int DstRowPitch = (*pWidth) * sizeof(DWORD); // = sizeof ARGB pixel. TODO : Is this correct?
int DstSlicePitch = DstRowPitch * (*pHeight); // TODO : Is this correct?
int DstSize = DstSlicePitch * DstDepth;
uint8_t* pDst = (uint8_t*)malloc(DstSize);
// And convert the source towards that buffer
/*ignore result*/ConvertD3DTextureToARGBBuffer(
X_Format,
pSrc, *pWidth, *pHeight, SrcRowPitch, SrcSlicePitch,
pDst, DstRowPitch, DstSlicePitch,
DstDepth,
pPalleteData);
// NOTE : Caller must take ownership!
return pDst;
}
void CxbxSetPixelContainerHeader
(
xbox::X_D3DPixelContainer* pPixelContainer,
DWORD Common,
UINT Width,
UINT Height,
UINT Levels,
xbox::X_D3DFORMAT Format,
UINT Dimensions,
UINT Pitch
)
{
// Set X_D3DResource field(s) :
pPixelContainer->Common = Common;
// DON'T SET pPixelContainer->Data
// DON'T SET pPixelContainer->Lock
// Are Width and Height both a power of two?
DWORD l2w; _BitScanReverse(&l2w, Width); // MSVC intrinsic; GCC has __builtin_clz
DWORD l2h; _BitScanReverse(&l2h, Height);
DWORD l2d = 0; // TODO : Set this via input argument
if (((1 << l2w) == Width) && ((1 << l2h) == Height)) {
Width = Height = Pitch = 1; // When setting Format, clear Size field
}
else {
l2w = l2h = l2d = 0; // When setting Size, clear D3DFORMAT_USIZE, VSIZE and PSIZE
}
// Set X_D3DPixelContainer field(s) :
pPixelContainer->Format = 0
| ((Dimensions << X_D3DFORMAT_DIMENSION_SHIFT) & X_D3DFORMAT_DIMENSION_MASK)
| (((DWORD)Format << X_D3DFORMAT_FORMAT_SHIFT) & X_D3DFORMAT_FORMAT_MASK)
| ((Levels << X_D3DFORMAT_MIPMAP_SHIFT) & X_D3DFORMAT_MIPMAP_MASK)
| ((l2w << X_D3DFORMAT_USIZE_SHIFT) & X_D3DFORMAT_USIZE_MASK)
| ((l2h << X_D3DFORMAT_VSIZE_SHIFT) & X_D3DFORMAT_VSIZE_MASK)
| ((l2d << X_D3DFORMAT_PSIZE_SHIFT) & X_D3DFORMAT_PSIZE_MASK)
;
pPixelContainer->Size = 0
| (((Width - 1) /*X_D3DSIZE_WIDTH_SHIFT*/) & X_D3DSIZE_WIDTH_MASK)
| (((Height - 1) << X_D3DSIZE_HEIGHT_SHIFT) & X_D3DSIZE_HEIGHT_MASK)
| (((Pitch - 1) << X_D3DSIZE_PITCH_SHIFT) & X_D3DSIZE_PITCH_MASK)
;
}
#if 0
/* Generic swizzle function, usable for both x and y dimensions.
When passing x, Max should be 2*height, and Shift should be 0

Some files were not shown because too many files have changed in this diff Show more