Compare commits

...

193 Commits

Author SHA1 Message Date
Inviuz b70a1edbc7 Mmapper with Neko's review fixes (#2503) 2017-03-12 03:15:22 +03:00
Nekotekina 14e5aa57c8 wx: UTF8 path fix 2017-03-12 00:41:48 +03:00
Nekotekina cdc49d166e Appveyor version fix 2017-03-11 22:06:12 +03:00
Nekotekina 7b65f93953 SPU ch69 2017-03-11 21:55:50 +03:00
Nekotekina d878a286bc 0.0.2 Alpha 2017-03-11 21:38:49 +03:00
Nekotekina ce35554a55 Fixes (sys_memory, sys_mmapper)
DeS
2017-03-11 21:37:18 +03:00
Nekotekina 1f17109203 __chkstk workaround 2017-03-11 19:49:32 +03:00
Nekotekina 4739eb3601 Reservation fix 2017-03-11 15:48:43 +03:00
Inviuz 40a84dd477 Handles directory creation in cellGameDataCheckCreate2 (#2488)
* Handle directory creation in cellGameDataCheckCreate2

Stops some games from displaying information about not enough memory on
hdd

* Returning CELL_OK causes some games to loop on sceNp functions

for "shaman magic"

* cellGameDataCheckCreate2 added param.sfo creating/rewriting

* fix fs::file null

and one readability change

* For debugging purposes

When fs::file problem is located will be improved

* Fixed wrong operators

* Conversion from vfs to fs

Should take care of fs::null

* Cleanup

removed some unnecessary logging

* Fix successive function calls

second call was always ending in error since it didn't create the conent
permission

* Changes according to Neko's review

* Change to use u32 value
2017-03-11 11:59:50 +03:00
Jake 309f409554 Fixes: cellMsgDialog, cellPad 2017-03-11 01:13:53 -06:00
Jake a6f8e4f6b3 Edat / Sdata: On the fly decryption for edat, fix EDAT_FLAG_0x20 decryption 2017-03-10 19:39:01 -06:00
kd-11 596db2c29c Merge pull request #2490 from kd-11/texture_cache_fix
gl: Fix texture cache bugs
2017-03-10 23:57:47 +03:00
kd-11 1fd33f611c gl: Fix texture cache bugs
Fix endianness bug
Fix r/w when real pitch is <= 64
2017-03-10 19:11:53 +03:00
Nekotekina 5e3bacbd9b New reservations
Memory system cleanup
sys_memory_get_page_attribute
2017-03-10 15:51:40 +03:00
Ani 7cdb5f3123 Add missing system languages
Adds Portuguese (BR) which was introduced in firmware 4.00 alongside
with English (UK)
Adds Turkish which was introduced in firmware 4.30
2017-03-08 19:59:34 +03:00
raven02 a168aa2df3 DX12: fix DRGB8 texture format (#2480)
* DX12: set native sampler format to BGRA8

* DX12: fix DRGB8 texture format
2017-03-07 19:34:09 +08:00
Jake 87fe93ee9a Mself / Sdata: on the fly partial decoding support (#2468) 2017-03-07 08:59:05 +08:00
kd-11 039e295e53 Merge pull request #2477 from kd-11/master
gl/vk/dx12: Implement forced channel remapping
2017-03-06 20:13:54 +03:00
kd-11 0fd888a42e vk: Fix DRGB8 format 2017-03-06 18:56:46 +03:00
kd-11 1e71285de8 gl/vk/dx12: Implement forced channel remapping
gcc fix
2017-03-06 14:34:29 +03:00
Nekotekina a2200bd01e Speculative fix for fmt::format 2017-03-05 21:42:54 +03:00
Nekotekina 9a9455a696 Fix #2462 2017-03-05 21:42:27 +03:00
Nekotekina 19a698682b Compilation fix (mingw) 2017-03-05 19:00:08 +03:00
Nekotekina 3baf79f929 ror64 added 2017-03-04 17:34:59 +03:00
Nekotekina 74d47943e9 sceNpDrmVerifyUpgradeLicense fixed 2017-03-04 17:14:51 +03:00
Nekotekina d9c78cf495 bf_t fix 2017-03-04 17:01:31 +03:00
Nekotekina 8e4a09d9e5 cellFsSdataOpenByFd draft
LLE-compatible implementation
lv2_file::make_view implemented
2017-03-04 16:54:49 +03:00
Wilfried Rabouin 67ac8bf070 PPUInterpreter: Fix undefined behavior of left rotate functions (#2469)
* PPUInterpreter: Fix undefined behavior of rol8 and rol16 with inline assembly

* PPUInterpreter: Fix undefined behavior of rol32 and rol64

* PPUInterpreter: Change left rotate functions to inline functions and move to types.h
2017-03-04 16:41:40 +03:00
Chocobo1 ef5225b776 TravisCI improvements (#2460)
* TravisCI: cleanup indentation

2 spaces for indentation

* TravisCI: make jobs drop to 3

TravisCI allocates 2 cpus per build instance

* TravisCI: move package installation to apt addon block

* TravisCI: make pip install to user directory

Also disable sudo for faster bootup

* TravisCI: expand if condition to multiple lines

Also remove outdated comment
2017-03-04 16:22:46 +03:00
Nekotekina 48b0583866 Default LLE option 2017-03-03 01:40:27 +03:00
Chocobo1 f528aada24 TravisCI: use ccache (#2448) 2017-03-03 00:49:42 +03:00
kd-11 1f45f05f8d Merge pull request #2447 from kd-11/cache_invalidation_fix
gl/vk/cache: Fix invalidating intersecting and overlapping memory regions
2017-03-01 14:44:14 +03:00
kd-11 b8fcbb68b0 gl/vk/cache: Fix invalidating intersecting and overlapping memory regions
whitespace fix
2017-03-01 14:00:06 +03:00
kd-11 4ab9a2a3a8 Merge pull request #2391 from kd-11/rsx_surface_tests
gl/vk/rsx: Improve texture cache
2017-03-01 10:36:03 +03:00
kd-11 85c0219aed vk: Fix cache range computation 2017-03-01 00:40:53 +03:00
kd-11 10e218e224 Fix linux build (travis-ci) 2017-03-01 00:40:52 +03:00
kd-11 69d044cd8d gl: Relax requirement to EXT_DSA from ARB_DSA to better support linux 2017-03-01 00:40:51 +03:00
kd-11 7062efeb3e gl: Fix a fence deadlock in fence::wait_for_signal
gl: Fix potential race condition when executing local work queue
2017-03-01 00:38:22 +03:00
kd-11 cee53fcecf gl: Ensure correct sized pbo is created upon reset 2017-03-01 00:16:55 +03:00
kd-11 ef8e92cbe3 gl: Change clear timing to immediate
gl: Handle cell write to locked RTT region

addendum: Fix buffer locking (personal 4)
2017-03-01 00:16:55 +03:00
kd-11 7c73c3b75c rsx/gl: Minor refactoring; prepare vulkan backend 2017-03-01 00:16:55 +03:00
kd-11 1e826f5ccf rsx: Minor optimization (tangible boost) 2017-03-01 00:16:55 +03:00
kd-11 96561c16ad gl: Rewrite texture cache
gl: scale downloaded RTT images
2017-03-01 00:16:55 +03:00
clienthax bd85f23ed9 Stub sys_gamepad ycon interface (#2435)
* Attempt to add sys_gamepad...

* Stub ycon methods

Signed-off-by: clienthax <clienthax@gmail.com>
2017-02-28 19:15:11 +03:00
Ani 557e1c3694 Fix cellDiscGameGetBootDiscInfo (#2430)
cellDiscGameGetBootDiscInfo is called by non-disc games for some reason.
That wasn't accounted for and therefore it would try to read PARAM.SFO
from an unmounted path and throw an access violation.

Tested with NBA Live 08 Demo NPUB90029, probably fixes similar games as
well
2017-02-28 18:42:45 +03:00
Nekotekina c785b14c18 LLVM fix 2017-02-28 17:16:32 +03:00
Nekotekina 3751d5d5c1 Fixed VFS initialization order
Fixed RAP location for SELF
Fixed custom config loading
2017-02-28 16:18:49 +03:00
Nekotekina 45bb51ded8 unpkg improved
Initial content_type parsing
Added PSP2 support
2017-02-28 16:18:48 +03:00
Jake c3b424eb11 rsx: image_in fix for clipx/y (#2440) 2017-02-28 16:21:07 +08:00
Ani 01ef9ecca4 sceNp: Fix ExitSpawn, Implement VerifyUpgradeLicense (#2428)
* sceNp: Fix ExitSpawn and ExitSpawn2

Fixes sceNpDrmProcessExitSpawn and sceNpDrmProcessExitSpawn2
functions

The problem was that first argument klicensee was missing, therefore
shifting every other argument out of place and throwing an access
violation at the end.

* Use npDrmIsAvailable on sceNpDrmProcessExitSpawn

Tries to decrypt DRM file with provided klicensee

* Implement sceNpDrmVerifyUpgradeLicense

Implements sceNpDrmVerifyUpgradeLicense / sceNpDrmVerifyUpgradeLicense2
2017-02-26 23:44:48 +03:00
Nekotekina 0eb6bf6a67 LLVM: splitting and caching 2017-02-26 20:14:18 +03:00
kd-11 4aab8db420 Merge pull request #2426 from kd-11/gl_rsx_wip
Implement BRB opcode
2017-02-26 14:42:01 +03:00
kd-11 9263999ad1 [rsx/vp] Improve BRB opcode implementation
fix merge issues
2017-02-26 10:17:34 +03:00
Ani 4d9b56c7f1 libnet: recvfrom() fix (#2427)
Prevents emulator crash when invalid socket is provided to the function
2017-02-25 23:27:31 +00:00
Nekotekina d0afef47f8 LLVM: block borders fixed 2017-02-25 21:30:28 +03:00
Nekotekina af53ce6f76 Stack overflow constrained 2017-02-25 19:29:31 +03:00
Nekotekina c0589cab05 Minor fix (linux) 2017-02-24 19:56:59 +03:00
Nekotekina f35babad98 shared_mutex fix (linux) 2017-02-24 18:48:53 +03:00
Nekotekina 8369cb2af6 Complex STOP 0x0 hack 2017-02-24 16:57:09 +03:00
Nekotekina 07d49b1bd8 sys_spu_thread_send_event fix 2017-02-24 16:57:09 +03:00
Nekotekina 5c8aab7917 Disable PPU interpreter (precise) 2017-02-24 16:57:09 +03:00
georgemoralis 14093c86a9 load config.yaml settings in a new configpergame 2017-02-24 16:55:27 +03:00
georgemoralis e153df4d5b pergameconfig : create directory if not exists 2017-02-24 16:55:27 +03:00
georgemoralis 3e0c356b0b per game config.yml (#2418) 2017-02-23 18:08:52 +03:00
Ani 5fce74a2a6 Hides D3D12 renderer on not supported OSes (#2416)
Since Windows 10 is the only system supporting it there's no point in it being listed on other OSes. 
Should prevent users reporting D3D12 isn't working on their non-w10 OSes.
2017-02-22 22:27:35 +00:00
Nekotekina 7c418f7ea5 PPU: block info fix 2017-02-23 00:35:29 +03:00
Nekotekina dac72ff371 Automatic dev_* dir creation 2017-02-22 17:29:10 +03:00
Nekotekina baf22527b0 Ditch fs::get_executable_dir 2017-02-22 17:17:26 +03:00
Nekotekina 4cba60b27f Cache decrypted EBOOT.BIN 2017-02-22 15:21:30 +03:00
Nekotekina 5e539f71e6 Prefer EBOOT.BIN 2017-02-22 13:23:21 +03:00
Nekotekina 9000407a77 cpu_thread::test_state added
lv2_obj::sleep adjustment
synchronization fixes
2017-02-22 13:23:21 +03:00
Nekotekina 4b6f8d2f62 fxm::check_unlocked added 2017-02-22 12:57:39 +03:00
Nekotekina 688097faa9 lf_spsc, lf_mpsc added 2017-02-22 12:57:08 +03:00
Nekotekina f08579d201 Log timestamp added 2017-02-22 12:56:34 +03:00
Jake 47fdaf6902 Improve cellPngDec (#2394)
* Improve cellPngDec
- ReadHeader and SetParam corrected
- Partial loading Implemented
- Interlace Support added
- Improve error handling

* Use custom exception

* Change to catch ref
2017-02-19 14:59:49 +03:00
kd-11 22c0f0d635 Merge pull request #2405 from jarveson/imagein-fix
Fix nv3089_image_in
2017-02-18 15:36:45 +03:00
Jake a886e28f87 Fix nv3089_image_in
- Restrict clip_x/y
- Remove double offset addition
2017-02-17 18:33:56 -06:00
Ani 65104b5909 Rough implementation of GCM_CONTEXT_DMA methods
Rough implementation of GCM_CONTEXT_DMA methods.
Fixes #1487
2017-02-17 22:35:28 +03:00
Ani 5605b4ee70 Fixes #2399
Defaults dGPU on NVIDIA Optimus systems and requests High Performance
mode on AMD systems.
2017-02-17 02:07:42 +03:00
Nekotekina 3ecba8e57f Fix #2397 2017-02-16 17:14:29 +03:00
flash-fire 0c8bda4f41 Simplify the popup menu. You can reuse the popup menu. So, no need to create/delete each time. Also, deleting stuff from a for each iterator is generally a bad idea. 2017-02-16 12:14:54 +03:00
Cornee Traas 458dbbd15d PS3UPDAT.PUP installer (#2386)
* Add PUP loader

* Add .tar loader and update .pup loader

* Add extract method + offset to TAR loader

Also adds error checking + operator bool overload

* Add firmware decryption keys to key vault

* Initial seperation of SELFDecrypter

This seperates SELFDecrypter into itself and SCEDecrypter.
SCEDecrypter contains the logic to decrypt any file with an SCE Header.
SELFDecrypter inherits from SCEDecrypter and contains the code
specifically to do with ELF. DecryptData could be deduplicated more.

* Add "Install Firmware" option to tools menu

* SCEDecrypter: put each segment in own file

Also, const-correctness, adjusted buffer size and better error handling

* More SELFDecrypter refactoring

* Compile fix

* Add messageboxes to firmware install

* Add progress bar to firmware install
2017-02-16 10:15:00 +08:00
Nekotekina b1aa87b515 Busy waiting added 2017-02-16 00:13:55 +03:00
B1ackDaemon b637bd3866 Merge pull request #2390 from georgemoralis/master
Implemented cellDiscGameGetBootDiscInfo
2017-02-15 12:57:31 +02:00
gmoralis b4557789f8 Implemented cellDiscGameGetBootDiscInfo 2017-02-15 10:49:18 +02:00
Ani 63169d7679 Nekotekina: Fix loading nonexistent libs
Nekotekina's fix for preventing loading nonexistent libs
2017-02-15 02:47:54 +03:00
georgemoralis 5d705c0e9c fix up an issue with sceNpTrophyCreateContext (#2382) 2017-02-14 20:46:56 +03:00
Zangetsu38 682924b51a D3D12: add DXGI_FORMAT_B8G8R8A8_UNORM missing (#2385) 2017-02-15 00:29:43 +08:00
Nekotekina 598c90f376 PPU thread scheduler 2017-02-13 22:26:11 +03:00
Nekotekina e4962054a4 LLVM: add check state 2017-02-13 20:51:56 +03:00
Nekotekina 436c245d13 Remove unused code 2017-02-13 18:33:31 +03:00
Nekotekina 8891d85f75 Hack for 0x0 2017-02-13 18:32:54 +03:00
Nekotekina 763550c472 0x2c8..0x2fc 2017-02-13 17:50:04 +03:00
Nekotekina d2ddb9882f SPU: IRET, SN event 2017-02-13 16:29:32 +03:00
Nekotekina 64ac6a59c4 REG_FUNC simplified 2017-02-13 16:29:32 +03:00
Nekotekina acd9d6ff24 Stub some modules 2017-02-12 21:35:55 +03:00
Nekotekina f4b95c0226 PPU analyser improved 2017-02-12 21:12:08 +03:00
Nekotekina 177084b1f4 cellSysutil functions
E1EC7B6A implemented
2017-02-12 21:03:08 +03:00
Nekotekina aff0484ea2 Stub some functions 2017-02-12 02:55:09 +03:00
Nekotekina ef1eff6d48 File: handle EACCES 2017-02-12 02:55:09 +03:00
clienthax b407d12ed1 Make APP packages bootable - from BlackDaemon (#2368) 2017-02-11 22:11:04 +03:00
Nekotekina e8bfce4ebd decrypt_self() function
Fixed SPU self decryption
Fixed PSV debug self load
2017-02-11 21:36:48 +03:00
Nekotekina be5f780977 fs::memory_stream fixed
seek() allows any offset
read() cannot overflow
2017-02-11 19:27:49 +03:00
Nekotekina 9e39c8cd64 fs::make_stream template 2017-02-11 19:05:35 +03:00
Nekotekina 3356a76c9b PPU fix 2017-02-11 17:17:07 +03:00
kd-11 e29747170f Merge pull request #2357 from kd-11/master
RTT shader access fix
2017-02-11 15:58:26 +03:00
kd-11 d6159a35aa gl/vk/dx12: Fix texture scaling on unnormalized rtt access 2017-02-11 15:45:59 +03:00
raven02 0019ea28c3 GUI: not displaying _INSTALL , _DATA and _GAMEDATA in gameviewer (#2364) 2017-02-11 16:50:48 +08:00
Nekotekina e899de88ff Log empty functions 2017-02-11 02:43:31 +03:00
Nekotekina bb111d325f memory fix 2017-02-10 23:27:58 +03:00
Zangetsu38 73906f9f29 d3d12: add x1r5g5b5_z1r5g5b5 and cleanup in D3D12Formats.
Add info in BufferUtils for log.
2017-02-10 22:04:45 +03:00
Nekotekina b20d7ff48c PPU breakpoints resurrected
Now with zero overhead
2017-02-10 16:33:22 +03:00
Nekotekina 8262d56574 PPU interpreter improved 2017-02-10 16:33:22 +03:00
Nekotekina 257b9a2015 Debugger fix
Crash fixes
GUI fixes
Debug enhancements
2017-02-10 16:33:22 +03:00
magiruuvelvet ff07595519 CMake: fix linkage on Linux, missing libdl 2017-02-10 16:24:31 +03:00
georgemoralis 6023794081 Joystick support using mmsystem library on windows (#2355)
* Joystick support using mmsystem library on windows

* fixed signed/unsigned issues with axes

* joystick buttons are now configurable
2017-02-10 15:28:00 +03:00
raven02 a1189fa37a finding from @3141card on PS3 console 2017-02-08 23:33:43 +03:00
raven02 5a2a2f49ed add missing LV2 error code 2017-02-08 23:33:43 +03:00
Ani 6c372d1781 cellL10n: Small adjustements (#2320)
l10n: Partial jstrnchk
l10n: SBCSstoUTF8s
l10n: Replace fmt::throw_exception() for cellL10n.todo() and
return ConversionOK for String methods, 0 for character methods
and the character itself for specific methods;
2017-02-08 03:25:10 +03:00
Oil 1669d0fdbd Minor changes (#2352)
* More initial values for rsx method registers

* Implemented spu decrementer event

* Remove u32 cast
2017-02-07 23:59:59 +03:00
Nekotekina 47b4c649f1 PPU fixes 2017-02-07 16:37:30 +03:00
Nekotekina ead67d8e67 Minor changes
Fix psv loader
2017-02-07 16:37:30 +03:00
☠Emulator_Team☠ 8c4ba3257c Some changes (#2350)
cellSysutilAvc2: Use UNIMPLEMENTED_FUNC macro instead of fmt::throw_exception.
2017-02-07 15:28:19 +03:00
ikki84 05c4aa5017 cellMsgDialogOpen: Go further using cellMsgDialogOpen2. (#2345)
Notes:
Needs proper implementation (!)
The functions shows similarities but the bits in argument "type" may vary a little.
Comparing both functions but testing in NPJA00014 I believe it could be acceptable until it be implemented properly

Feel free to reject if it´s not allowed

Update cellMsgDialog.cpp
2017-02-07 13:00:19 +03:00
raven02 f84b2f072f D3D12: add missing logic set 2017-02-06 12:34:20 +03:00
ikki84 9b122e5449 cellMusic.cpp Change Throw Exception into UNIMPLEMENTED_FUNC
Also return CELL_OK in function until the it become properly developed.
2017-02-05 19:39:21 +03:00
Nekotekina 246b9f3182 CHECK_EMU_STATUS removal 2017-02-05 17:35:27 +03:00
Nekotekina bf6854d121 LV2_LOCK removed 2017-02-05 16:35:10 +03:00
Nekotekina eebd48e51d cellHddGameGetSizeKB w/a 2017-02-05 16:07:26 +03:00
Nekotekina 9767cc84b0 16b atomic load fix 2017-02-05 15:49:51 +03:00
Nekotekina 11b0b6958f sys_spu diag 2017-02-05 15:48:11 +03:00
Nekotekina cb1f30e9d2 Report fixup 2017-02-05 03:06:05 +03:00
Nekotekina ec943b38a2 sys_spu... 2017-02-05 02:26:57 +03:00
Nekotekina 68f0393cf3 sys_interrupt... 2017-02-04 20:39:04 +03:00
Nekotekina fe26db4d36 idm::last_id cleanup 2017-02-04 20:39:04 +03:00
Nekotekina e3e4decabf sys_timer... 2017-02-04 20:39:04 +03:00
Nekotekina 6537909fd2 sys_event_queue... 2017-02-04 20:39:04 +03:00
Nekotekina 26ced19e18 sys_event_flag... 2017-02-04 20:39:03 +03:00
Nekotekina 3d85a89cc9 sys_lwcond, sys_lwmutex 2017-02-04 20:39:03 +03:00
Nekotekina 16944b0c4c sys_cond, sys_mutex 2017-02-04 20:39:03 +03:00
Nekotekina c4cc3ad81e sys_rwlock... 2017-02-04 20:39:03 +03:00
Nekotekina 0674a58502 idm::check extended
idm::check_unlocked
idm::find_unlocked
idm::get_unlocked
2017-02-04 20:39:03 +03:00
O1L a809f33418 GL: try to skip incorrectly prepared FBO's. 2017-02-04 19:32:54 +03:00
O1L bf33a1827c Set default values for NV4097_SET_SURFACE_FORMAT register 2017-02-04 19:32:54 +03:00
O1L 57ef6dae91 Implemented cellGcmSetDefaultCommandBufferAndSegmentWordSize 2017-02-04 19:32:54 +03:00
Yukarin 758f1bcdee Files clean up 2017-02-03 21:15:46 +03:00
Yukarin fa6fd5980e Fix rpcs3qt Linux build 2017-02-03 21:15:46 +03:00
Robbie 3762f1e50d Make RPCS3 compatible with QT5.8 (#2330) 2017-02-03 11:20:23 +03:00
ikki84 5aaee670d1 Update ErrorCodes.h (#2332) 2017-02-03 02:10:16 +03:00
Ani 9916f0e5c5 [O1L] cellVideoOut: X8B8G8R8 fix
O1L's fix for cellVideoOut.
2017-02-02 21:15:40 +03:00
gmoralis 9eba0ac58c proper error code when file not found . This should fix issue #1923 at least bomberman works 2017-02-01 14:22:30 +03:00
Zangetsu38 13493ec051 Add Patreon link on about dialog (#2310) 2017-02-01 12:37:59 +08:00
Nekotekina b7a7a5c582 #2314 fix 2017-01-31 20:37:02 +03:00
Nekotekina 213527ca71 sys_semaphore... 2017-01-31 19:36:17 +03:00
Nekotekina dc7ac22f84 sys_ppu
LV2_LOCK removal
error_code introduction
2017-01-31 18:03:09 +03:00
kd-11 16d1577722 Merge pull request #2312 from Zangetsu38/d3d12
rsx: Add support for CELL_GCM_CONTEXT_DMA_MEMORY_HOST_BUFFER
2017-01-31 17:09:46 +03:00
Zangetsu38 bd6a7dea7e rsx: Fix error context dma 2017-01-31 14:37:31 +01:00
Nekotekina 6433eebe1e IdManager fix 2017-01-31 15:57:32 +03:00
Ani 35b7cf4c98 Misc changes (#2309)
AppVeyor: Improve artifact name
Travis: Update notification email
README: Fix forum link and remove coveralls
sceNp2: Use UNIMPLEMENTED_FUNC macro instead of fmt::throw_exception
VK: Fix pathing for linux
AboutDialog: Fix forum link
2017-01-31 01:16:47 +03:00
Nekotekina c733d03525 cellVdec mem limit 2017-01-30 01:50:20 +03:00
Nekotekina aef5113d49 Fix #2300 2017-01-29 22:52:07 +03:00
Nekotekina b21fce4d6f IdManager improved
lv2_obj for kernel objects
Simple lookup (vector)
Another idm API refactoring
2017-01-29 21:37:04 +03:00
Nekotekina be8d8ded3f replace high_resolution_clock 2017-01-29 19:52:19 +03:00
Nekotekina a5a2d43d7c Thread.cpp refinement
Hide thread mutex
Safe notify() method
Other refactoring
2017-01-29 19:52:19 +03:00
Zangetsu38 da878c36bd fix osk dialog (#2302) 2017-01-29 13:21:58 +08:00
Ryan Gonzalez 11e2965b5c Switch to always using a steady clock (closes #2200) 2017-01-29 03:07:08 +03:00
Nekotekina 4203f53b67 IdManager bugfixed 2017-01-28 17:59:43 +03:00
Nekotekina d4c3905355 Debug build fix 2017-01-28 15:32:45 +03:00
Nekotekina 307da1fe39 Fixed fs::file::open 2017-01-28 13:11:06 +03:00
Nekotekina 44b2df27ea FPS counter fix 2017-01-28 03:21:11 +03:00
Nekotekina 5ee0cea672 cellAudio fix 2017-01-28 03:21:11 +03:00
Nekotekina df6607e236 Minor fixes 2017-01-28 03:21:11 +03:00
Nekotekina e32c49c12c vcxproj fix 2017-01-28 03:21:11 +03:00
Nekotekina 8693031b06 cellVdec: minor fix 2017-01-28 03:21:10 +03:00
Ryan Gonzalez b644d5b2ae Add logic_set support to VKGSRender 2017-01-28 03:20:30 +03:00
Ani 5e8852af80 sceNp: Register missing functions (#2291)
Registers missing named functions exported from sceNp on firmware 4.76
2017-01-27 20:17:34 +03:00
Zangetsu38 b6f8eface7 Register missing cellSysutil and sysPrxForUser function. 2017-01-27 17:27:22 +03:00
Cornee Traas fe23e0cc6e Un-malloc Crypto 2017-01-27 00:38:58 +03:00
Nekotekina bdca52a95d cellVdec: 2997/50 support 2017-01-26 20:01:16 +03:00
Nekotekina 33d2618e5b cellOskDialog fixes 2017-01-26 20:01:16 +03:00
Nekotekina 2bc8723824 cellVdecDecodeAu mode implemented 2017-01-26 20:01:16 +03:00
Nekotekina 4ecf05aa13 cellFsUtime implemented 2017-01-26 20:01:16 +03:00
Nekotekina 1705638e25 MFVSCR error removed 2017-01-26 20:01:16 +03:00
Nekotekina 5db0f47710 lv2_fs_object fix 2017-01-26 04:22:15 +03:00
Nekotekina ee173dc3a2 IdManager fix 2017-01-26 04:22:15 +03:00
Nekotekina 591a6c8671 IdManager improved 2017-01-25 23:29:36 +03:00
Zangetsu38 6c189511af Fix corect number branch release llvm (#2285) 2017-01-25 20:32:55 +03:00
Nekotekina a06223b844 IdManager sugar 2017-01-25 04:43:12 +03:00
Nekotekina 9232ddf0ab Minor changes 2017-01-25 04:43:12 +03:00
Nekotekina 1c14d872a8 New shared_mutex
Experimental sync utils
New semaphore<>
New cond_variable
New owned_mutex
2017-01-25 00:38:07 +03:00
Nekotekina 98fc131d47 util::dynamic_import
Futex implementation
2017-01-25 00:38:07 +03:00
Nekotekina 8ad31d2559 Test 2017-01-25 00:38:07 +03:00
Ivan 46dde3f8f7 Fixes (#2279)
Fixed appveyor icon
PPUAnalyser fix
OSK dialog fix
2017-01-24 00:56:27 +03:00
316 changed files with 16959 additions and 10839 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ IndentWidth: 1
AccessModifierOffset: -1
PointerAlignment: Left
NamespaceIndentation: All
ColumnLimit: 100
ColumnLimit: 200
BreakBeforeBraces: Allman
BreakConstructorInitializersBeforeComma: true
BreakBeforeBinaryOperators: false
+1 -1
View File
@@ -12,7 +12,7 @@
[submodule "llvm"]
path = llvm
url = https://github.com/llvm-mirror/llvm
branch = release_36
branch = release_38
[submodule "rsx_program_decompiler"]
path = rsx_program_decompiler
url = https://github.com/RPCS3/rsx_program_decompiler
+54 -48
View File
@@ -1,90 +1,96 @@
language: cpp
sudo: required
sudo: false
dist: trusty
os:
- linux
# - osx
- linux
#- osx
osx_image: xcode6.4
compiler:
- clang
- gcc
- clang
- gcc
cache:
ccache: true
directories:
- $HOME/hombebrew_cache
env:
global:
- secure: "Vf+FY48nip9JppMnq11105NealdErSWsoUhHo63/V3V+LKfA9guenxCp93/qoSIdSGC/sJwb0yIIMGvkTT/rxDJNh6Z+BWUTb2E0WEIIQbvTJNOSUzoq7dfF1LT61XjVjByFzcbC2xjtaBowmcAYEs1jGUUuEjYVCMmD5lY8hUg="
# Which Travis environment to run Coverity on
- coverity_scan_run_condition='"$TRAVIS_OS_NAME" = linux -a "$CC" = gcc'
# Test mode is for testing if it's working with Coverity. Change to true if testing, to avoid reaching the quota.
- coverity_scan_script_test_mode=false
- secure: "Vf+FY48nip9JppMnq11105NealdErSWsoUhHo63/V3V+LKfA9guenxCp93/qoSIdSGC/sJwb0yIIMGvkTT/rxDJNh6Z+BWUTb2E0WEIIQbvTJNOSUzoq7dfF1LT61XjVjByFzcbC2xjtaBowmcAYEs1jGUUuEjYVCMmD5lY8hUg="
# Which Travis environment to run Coverity on
- coverity_scan_run_condition='"$TRAVIS_OS_NAME" = linux -a "$CC" = gcc'
# Test mode is for testing if it's working with Coverity. Change to true if testing, to avoid reaching the quota.
- coverity_scan_script_test_mode=false
matrix:
exclude:
- os: osx
compiler: gcc
exclude:
- os: osx
compiler: gcc
git:
submodules: false
before_install:
# shutdown services on Travis, which may have a memory impact
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo apt-get install -y libwxgtk3.0-dev;
fi;
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01';
fi;
# Add coverall for C++ so coverall.io could be triggered. Even it should be --coverage and gcov.
# Add coverall for C++ so coverall.io could be triggered. Even it should be --coverage and gcov.
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo pip install cpp-coveralls requests[security];
pip install --user cpp-coveralls requests[security];
else
brew update; brew update;
brew install glew wxwidgets llvm38;
brew install ccache glew wxwidgets llvm38;
fi;
before_script:
- git submodule update --init rsx_program_decompiler asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal
- mkdir build
- cd build
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cmake ..; else cmake .. -DLLVM_DIR=/usr/local/opt/llvm38/lib/llvm-3.8/share/llvm/cmake; fi
- git submodule update --init rsx_program_decompiler asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal
- mkdir build
- cd build
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
cmake ..;
else
cmake .. -DLLVM_DIR=/usr/local/opt/llvm38/lib/llvm-3.8/share/llvm/cmake;
fi;
script:
# Add a command to show all the variables. May be useful for debugging Travis.
# - echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
# And to ensure the versions of toolchain
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j 4; fi
# Add a command to show all the variables. May be useful for debugging Travis.
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
# And to ensure the versions of toolchain
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j 3; fi
addons:
apt:
sources:
- ubuntu-toolchain-r-test
# - llvm-toolchain-trusty-3.8 temporarily disabled
- ubuntu-toolchain-r-test
#- llvm-toolchain-trusty-3.8 temporarily disabled
packages:
- cmake
- libopenal-dev
- freeglut3-dev
- libglew-dev
- libc6-dev
# - llvm-3.8
# - llvm-3.8-dev
- libedit-dev
- g++-5
- gcc-5
# - clang-3.6
- libstdc++-5-dev
- lib32stdc++6
- zlib1g-dev
- cmake
- libopenal-dev
- freeglut3-dev
- libglew-dev
- libc6-dev
#- llvm-3.8
#- llvm-3.8-dev
- libedit-dev
- g++-5
- gcc-5
#- clang-3.6
- libstdc++-5-dev
- lib32stdc++6
- zlib1g-dev
- libwxgtk3.0-dev
coverity_scan:
project:
name: $TRAVIS_REPO_SLUG
description: "PS3 emulator/debugger"
notification_email: raul.tambre@gmail.com
build_command: "make -j 4"
notification_email: nekotekina@gmail.com
build_command: "make -j 3"
branch_pattern: coverity_scan
after_success:
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "$TRAVIS_OS_NAME" = linux ]; then coveralls --extension .c --extension .cpp --extension .h; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "$TRAVIS_OS_NAME" = linux ]; then coveralls --extension .c --extension .cpp --extension .h; fi
+6
View File
@@ -19,6 +19,12 @@ if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 6.1)
message( FATAL_ERROR "RPCS3 can't be compiled with gcc-6.1, see #1691." )
endif()
find_program(CCACHE_FOUND ccache)
if (CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif()
add_definitions(-DCMAKE_BUILD)
# We use libpng's static library and don't need to build the shared library and run the tests
+1 -2
View File
@@ -4,11 +4,10 @@ RPCS3
[![Build Status](https://travis-ci.org/RPCS3/rpcs3.svg?branch=master)](https://travis-ci.org/RPCS3/rpcs3)
[![Build status](https://ci.appveyor.com/api/projects/status/411c4clmiohtx7eo/branch/master?svg=true)](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
[![Coverity Status](https://img.shields.io/coverity/scan/3960.svg)](https://scan.coverity.com/projects/3960)
[![Coverage Status](https://coveralls.io/repos/RPCS3/rpcs3/badge.svg)](https://coveralls.io/r/RPCS3/rpcs3)
An open-source PlayStation 3 emulator/debugger written in C++.
You can find some basic information in the [FAQ](https://github.com/RPCS3/rpcs3/wiki/FAQ). For discussion about this emulator and PS3 emulation please visit the [official forums](http://www.emunewz.net/forum/forumdisplay.php?fid=162).
You can find some basic information in the [FAQ](https://github.com/RPCS3/rpcs3/wiki/FAQ). For discussion about this emulator and PS3 emulation please visit the [official forums](http://www.emunewz.net/forum/forumdisplay.php?fid=172).
Official Discord server: https://discord.me/RPCS3
+13 -7
View File
@@ -300,21 +300,24 @@ struct atomic_storage<T, 2> : atomic_storage<T, 0>
static inline bool bts(T& dest, uint bit)
{
bool result;
__asm__("lock btsw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
ushort _bit = (ushort)bit;
__asm__("lock btsw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
return result;
}
static inline bool btr(T& dest, uint bit)
{
bool result;
__asm__("lock btrw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
ushort _bit = (ushort)bit;
__asm__("lock btrw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
return result;
}
static inline bool btc(T& dest, uint bit)
{
bool result;
__asm__("lock btcw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
ushort _bit = (ushort)bit;
__asm__("lock btcw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
return result;
}
#endif
@@ -498,21 +501,24 @@ struct atomic_storage<T, 8> : atomic_storage<T, 0>
static inline bool bts(T& dest, uint bit)
{
bool result;
__asm__("lock btsq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
ullong _bit = bit;
__asm__("lock btsq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
return result;
}
static inline bool btr(T& dest, uint bit)
{
bool result;
__asm__("lock btrq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
ullong _bit = bit;
__asm__("lock btrq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
return result;
}
static inline bool btc(T& dest, uint bit)
{
bool result;
__asm__("lock btcq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
ullong _bit = bit;
__asm__("lock btcq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
return result;
}
#endif
@@ -530,7 +536,7 @@ struct atomic_storage<T, 16> : atomic_storage<T, 0>
static inline T load(const T& dest)
{
llong result[2];
llong result[2]{0, 0};
_InterlockedCompareExchange128((volatile llong*)&dest, 0, 0, result);
return *(T*)+result;
}
+21 -18
View File
@@ -7,6 +7,7 @@ struct bf_base
{
using type = T;
using vtype = simple_t<type>;
using utype = typename std::make_unsigned<vtype>::type;
// Datatype bitsize
static constexpr uint bitmax = sizeof(T) * 8; static_assert(N - 1 < bitmax, "bf_base<> error: N out of bounds");
@@ -15,7 +16,7 @@ struct bf_base
static constexpr uint bitsize = N;
// Value mask
static constexpr vtype vmask = static_cast<vtype>(~std::make_unsigned_t<vtype>{} >> (bitmax - bitsize));
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
protected:
type m_data;
@@ -27,14 +28,15 @@ struct bf_t : bf_base<T, N>
{
using type = typename bf_t::type;
using vtype = typename bf_t::vtype;
using utype = typename bf_t::utype;
// Field offset
static constexpr uint bitpos = I; static_assert(bitpos + N <= bf_t::bitmax, "bf_t<> error: I out of bounds");
// Get bitmask of size N, at I pos
static constexpr vtype data_mask()
static constexpr utype data_mask()
{
return bf_t::vmask << bitpos;
return static_cast<utype>(~utype{} >> (bf_t::bitmax - bf_t::bitsize) << bitpos);
}
// Bitfield extraction helper
@@ -50,7 +52,7 @@ struct bf_t : bf_base<T, N>
// Load unsigned value
static constexpr T2 extract(const T& data)
{
return (data >> bitpos) & bf_t::vmask;
return static_cast<T2>((static_cast<utype>(data) >> bitpos) & bf_t::vmask);
}
};
@@ -60,7 +62,7 @@ struct bf_t : bf_base<T, N>
// Load signed value (sign-extended)
static constexpr T2 extract(const T& data)
{
return data << (bf_t::bitmax - bitpos - N) >> (bf_t::bitmax - N);
return static_cast<T2>(static_cast<vtype>(static_cast<utype>(data) << (bf_t::bitmax - bitpos - N)) >> (bf_t::bitmax - N));
}
};
@@ -73,7 +75,7 @@ struct bf_t : bf_base<T, N>
// Bitfield insertion
static constexpr vtype insert(vtype value)
{
return (value & bf_t::vmask) << bitpos;
return static_cast<vtype>((value & bf_t::vmask) << bitpos);
}
// Load bitfield value
@@ -85,7 +87,7 @@ struct bf_t : bf_base<T, N>
// Load raw data with mask applied
constexpr T unshifted() const
{
return this->m_data & data_mask();
return static_cast<T>(this->m_data & data_mask());
}
// Optimized bool conversion (must be removed if inappropriate)
@@ -97,14 +99,14 @@ struct bf_t : bf_base<T, N>
// Store bitfield value
bf_t& operator =(vtype value)
{
this->m_data = (this->m_data & ~data_mask()) | insert(value);
this->m_data = static_cast<vtype>((this->m_data & ~data_mask()) | insert(value));
return *this;
}
vtype operator ++(int)
{
vtype result = *this;
*this = result + 1;
utype result = *this;
*this = static_cast<vtype>(result + 1);
return result;
}
@@ -115,8 +117,8 @@ struct bf_t : bf_base<T, N>
vtype operator --(int)
{
vtype result = *this;
*this = result - 1;
utype result = *this;
*this = static_cast<vtype>(result - 1);
return result;
}
@@ -142,19 +144,19 @@ struct bf_t : bf_base<T, N>
bf_t& operator &=(vtype right)
{
this->m_data &= (right & bf_t::vmask) << bitpos;
this->m_data &= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
return *this;
}
bf_t& operator |=(vtype right)
{
this->m_data |= (right & bf_t::vmask) << bitpos;
this->m_data |= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
return *this;
}
bf_t& operator ^=(vtype right)
{
this->m_data ^= (right & bf_t::vmask) << bitpos;
this->m_data ^= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
return *this;
}
};
@@ -165,23 +167,24 @@ struct cf_t : bf_base<typename F::type, F::bitsize + cf_t<Fields...>::bitsize>
{
using type = typename cf_t::type;
using vtype = typename cf_t::vtype;
using utype = typename cf_t::utype;
// Get disjunction of all "data" masks of concatenated values
static constexpr vtype data_mask()
{
return F::data_mask() | cf_t<Fields...>::data_mask();
return static_cast<vtype>(F::data_mask() | cf_t<Fields...>::data_mask());
}
// Extract all bitfields and concatenate
static constexpr vtype extract(const type& data)
{
return F::extract(data) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data);
return static_cast<vtype>(static_cast<utype>(F::extract(data)) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data));
}
// Split bitfields and insert them
static constexpr vtype insert(vtype value)
{
return F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value);
return static_cast<vtype>(F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value));
}
// Load value
+16
View File
@@ -261,6 +261,22 @@ void cfg::node::from_default()
}
}
void cfg::bool_entry::from_default()
{
value = def;
}
void cfg::string_entry::from_default()
{
*this = def;
}
void cfg::set_entry::from_default()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_set = {};
}
cfg::root_node& cfg::get_root()
{
// Magic static
+3 -13
View File
@@ -141,10 +141,7 @@ namespace cfg
return *this;
}
void from_default() override
{
value = def;
}
void from_default() override;
std::string to_string() const override
{
@@ -428,10 +425,7 @@ namespace cfg
return m_value.size();
}
void from_default() override
{
*this = def;
}
void from_default() override;
std::string to_string() const override
{
@@ -473,11 +467,7 @@ namespace cfg
m_set = std::move(set);
}
void from_default() override
{
std::lock_guard<std::mutex> lock(m_mutex);
m_set = {};
}
void from_default() override;
std::vector<std::string> to_list() const override
{
+107 -76
View File
@@ -1,6 +1,6 @@
#include "File.h"
#include "mutex.h"
#include "StrFmt.h"
#include "SharedMutex.h"
#include "BEType.h"
#include "Crypto/sha1.h"
@@ -69,12 +69,22 @@ static time_t to_time(const FILETIME& ft)
return to_time(v);
}
static FILETIME from_time(s64 _time)
{
const ullong wtime = (_time + 11644473600ULL) * 10000000ULL;
FILETIME result;
result.dwLowDateTime = static_cast<DWORD>(wtime);
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
return result;
}
static fs::error to_error(DWORD e)
{
switch (e)
{
case ERROR_FILE_NOT_FOUND: return fs::error::noent;
case ERROR_PATH_NOT_FOUND: return fs::error::noent;
case ERROR_ACCESS_DENIED: return fs::error::acces;
case ERROR_ALREADY_EXISTS: return fs::error::exist;
case ERROR_FILE_EXISTS: return fs::error::exist;
case ERROR_NEGATIVE_SEEK: return fs::error::inval;
@@ -94,6 +104,7 @@ static fs::error to_error(DWORD e)
#include <libgen.h>
#include <string.h>
#include <unistd.h>
#include <utime.h>
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <copyfile.h>
@@ -109,6 +120,7 @@ static fs::error to_error(int e)
case ENOENT: return fs::error::noent;
case EEXIST: return fs::error::exist;
case EINVAL: return fs::error::inval;
case EACCES: return fs::error::acces;
default: fmt::throw_exception("Unknown system error: %d.", e);
}
}
@@ -136,6 +148,18 @@ namespace fs
static device_manager instance;
return instance;
}
file_base::~file_base()
{
}
dir_base::~dir_base()
{
}
device_base::~device_base()
{
}
}
std::shared_ptr<fs::device_base> fs::device_manager::get_device(const std::string& path)
@@ -591,6 +615,48 @@ bool fs::truncate_file(const std::string& path, u64 length)
#endif
}
bool fs::utime(const std::string& path, s64 atime, s64 mtime)
{
if (auto device = get_virtual_device(path))
{
return device->utime(path, atime, mtime);
}
#ifdef _WIN32
// Open the file
const auto handle = CreateFileW(to_wchar(path).get(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
g_tls_error = to_error(GetLastError());
return false;
}
FILETIME _atime = from_time(atime);
FILETIME _mtime = from_time(mtime);
if (!SetFileTime(handle, nullptr, &_atime, &_mtime))
{
g_tls_error = to_error(GetLastError());
CloseHandle(handle);
return false;
}
CloseHandle(handle);
return true;
#else
::utimbuf buf;
buf.actime = atime;
buf.modtime = mtime;
if (::utime(path.c_str(), &buf) != 0)
{
g_tls_error = to_error(errno);
return false;
}
return true;
#endif
}
void fs::file::xnull() const
{
fmt::throw_exception<std::logic_error>("fs::file is null");
@@ -601,17 +667,17 @@ void fs::file::xfail() const
fmt::throw_exception("Unexpected fs::error %s", g_tls_error);
}
bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
fs::file::file(const std::string& path, bs_t<open_mode> mode)
{
if (auto device = get_virtual_device(path))
{
if (auto&& _file = device->open(path, mode))
{
m_file = std::move(_file);
return true;
return;
}
return false;
return;
}
#ifdef _WIN32
@@ -631,7 +697,7 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
if (test(mode & fs::excl))
{
g_tls_error = error::inval;
return false;
return;
}
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
@@ -642,7 +708,7 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
if (handle == INVALID_HANDLE_VALUE)
{
g_tls_error = to_error(GetLastError());
return false;
return;
}
class windows_file final : public file_base
@@ -763,7 +829,7 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
if (fd == -1)
{
g_tls_error = to_error(errno);
return false;
return;
}
class unix_file final : public file_base
@@ -849,8 +915,6 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
m_file = std::make_unique<unix_file>(fd);
#endif
return true;
}
fs::file::file(const void* ptr, std::size_t size)
@@ -871,36 +935,42 @@ fs::file::file(const void* ptr, std::size_t size)
fs::stat_t stat() override
{
fmt::throw_exception<std::logic_error>("Not supported" HERE);
fmt::raw_error("fs::file::memory_stream::stat(): not supported");
}
bool trunc(u64 length) override
{
fmt::throw_exception<std::logic_error>("Not allowed" HERE);
return false;
}
u64 read(void* buffer, u64 count) override
{
const u64 start = m_pos;
const u64 end = seek(count, fs::seek_cur);
if (end < start) fmt::throw_exception<std::logic_error>("Stream overflow" HERE);
const u64 read_size = end - start;
std::memcpy(buffer, m_ptr + start, read_size);
return read_size;
if (m_pos < m_size)
{
// Get readable size
if (const u64 result = std::min<u64>(count, m_size - m_pos))
{
std::memcpy(buffer, m_ptr + m_pos, result);
m_pos += result;
return result;
}
}
return 0;
}
u64 write(const void* buffer, u64 count) override
{
fmt::throw_exception<std::logic_error>("Not allowed" HERE);
return 0;
}
u64 seek(s64 offset, fs::seek_mode whence) override
{
return
whence == fs::seek_set ? m_pos = std::min<u64>(offset, m_size) :
whence == fs::seek_cur ? m_pos = std::min<u64>(offset + m_pos, m_size) :
whence == fs::seek_end ? m_pos = std::min<u64>(offset + m_size, m_size) :
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
whence == fs::seek_set ? m_pos = offset :
whence == fs::seek_cur ? m_pos = offset + m_pos :
whence == fs::seek_end ? m_pos = offset + m_size :
(fmt::raw_error("fs::file::memory_stream::seek(): invalid whence"), 0);
}
u64 size() override
@@ -1055,11 +1125,22 @@ const std::string& fs::get_config_dir()
// Use magic static
static const std::string s_dir = []
{
#ifdef _WIN32
return get_executable_dir(); // ?
#else
std::string dir;
#ifdef _WIN32
wchar_t buf[2048];
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
{
MessageBoxA(0, fmt::format("GetModuleFileName() failed: error %u.", GetLastError()).c_str(), "fs::get_config_dir()", MB_ICONERROR);
return dir; // empty
}
to_utf8(dir, buf); // Convert to UTF-8
std::replace(dir.begin(), dir.end(), '\\', '/');
dir.resize(dir.rfind('/') + 1);
#else
if (const char* home = ::getenv("XDG_CONFIG_HOME"))
dir = home;
else if (const char* home = ::getenv("HOME"))
@@ -1072,59 +1153,9 @@ const std::string& fs::get_config_dir()
if (!is_dir(dir) && !create_path(dir))
{
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
return get_executable_dir();
}
return dir;
#endif
}();
return s_dir;
}
const std::string& fs::get_executable_dir()
{
// Use magic static
static const std::string s_dir = []
{
std::string dir;
#ifdef _WIN32
wchar_t buf[2048];
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
{
MessageBoxA(0, fmt::format("GetModuleFileName() failed: error %u.", GetLastError()).c_str(), "fs::get_executable_dir()", MB_ICONERROR);
return dir; // empty
}
to_utf8(dir, buf); // Convert to UTF-8
std::replace(dir.begin(), dir.end(), '\\', '/');
#elif __APPLE__
char buf[4096];
u32 size = sizeof(buf);
if (_NSGetExecutablePath(buf, &size))
{
std::printf("_NSGetExecutablePath() failed (size=0x%x).\n", size);
return dir; // empty
}
dir = buf;
#else
char buf[4096];
const auto size = ::readlink("/proc/self/exe", buf, sizeof(buf));
if (size <= 0 || size >= sizeof(buf))
{
std::printf("readlink(/proc/self/exe) failed (%d).\n", errno);
return dir; // empty
}
dir.assign(buf, size);
#endif
// Leave only path
dir.resize(dir.rfind('/') + 1);
return dir;
}();
@@ -1135,7 +1166,7 @@ std::string fs::get_data_dir(const std::string& prefix, const std::string& locat
{
static const std::string s_dir = []
{
const std::string& dir = get_config_dir() + "/data/";
const std::string& dir = get_config_dir() + "data/";
if (!is_dir(dir) && !create_path(dir))
{
+117 -13
View File
@@ -6,6 +6,7 @@
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
namespace fs
{
@@ -57,7 +58,7 @@ namespace fs
// File handle base
struct file_base
{
virtual ~file_base() = default;
virtual ~file_base();
virtual stat_t stat() = 0;
virtual bool trunc(u64 length) = 0;
@@ -76,7 +77,7 @@ namespace fs
// Directory handle base
struct dir_base
{
virtual ~dir_base() = default;
virtual ~dir_base();
virtual bool read(dir_entry&) = 0;
virtual void rewind() = 0;
@@ -85,7 +86,7 @@ namespace fs
// Virtual device
struct device_base
{
virtual ~device_base() = default;
virtual ~device_base();
virtual bool stat(const std::string& path, stat_t& info) = 0;
virtual bool remove_dir(const std::string& path) = 0;
@@ -93,6 +94,7 @@ namespace fs
virtual bool rename(const std::string& from, const std::string& to) = 0;
virtual bool remove(const std::string& path) = 0;
virtual bool trunc(const std::string& path, u64 length) = 0;
virtual bool utime(const std::string& path, s64 atime, s64 mtime) = 0;
virtual std::unique_ptr<file_base> open(const std::string& path, bs_t<open_mode> mode) = 0;
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
@@ -140,6 +142,9 @@ namespace fs
// Change file size (possibly appending zeros)
bool truncate_file(const std::string& path, u64 length);
// Set file access/modification time
bool utime(const std::string& path, s64 atime, s64 mtime);
class file final
{
std::unique_ptr<file_base> m_file;
@@ -152,17 +157,19 @@ namespace fs
file() = default;
// Open file with specified mode
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read)
{
open(path, mode);
}
// Open file with specified mode
bool open(const std::string& path, bs_t<open_mode> mode = ::fs::read);
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
// Open memory for read
explicit file(const void* ptr, std::size_t size);
// Open file with specified args (forward to constructor)
template <typename... Args>
bool open(Args&&... args)
{
*this = fs::file(std::forward<Args>(args)...);
return m_file.operator bool();
}
// Check whether the handle is valid (opened file)
explicit operator bool() const
{
@@ -437,9 +444,6 @@ namespace fs
// Get configuration directory
const std::string& get_config_dir();
// Get executable directory
const std::string& get_executable_dir();
// Get data/cache directory for specified prefix and suffix
std::string get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix);
@@ -459,8 +463,108 @@ namespace fs
inval,
noent,
exist,
acces,
};
// Error code returned
extern thread_local error g_tls_error;
template <typename T>
struct container_stream final : file_base
{
// T can be a reference, but this is not recommended
using value_type = typename std::remove_reference_t<T>::value_type;
T obj;
u64 pos;
container_stream(T&& obj)
: obj(std::forward<T>(obj))
, pos(0)
{
}
~container_stream() override
{
}
stat_t stat() override
{
fmt::raw_error("fs::container_stream<>::stat(): not supported");
}
bool trunc(u64 length) override
{
obj.resize(length);
return true;
}
u64 read(void* buffer, u64 size) override
{
const u64 end = obj.size();
if (pos < end)
{
// Get readable size
if (const u64 max = std::min<u64>(size, end - pos))
{
std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max, static_cast<value_type*>(buffer));
pos = pos + max;
return max;
}
}
return 0;
}
u64 write(const void* buffer, u64 size) override
{
const u64 old_size = obj.size();
if (old_size + size < old_size)
{
fmt::raw_error("fs::container_stream<>::write(): overflow");
}
if (pos > old_size)
{
// Fill gap if necessary (default-initialized)
obj.resize(pos);
}
const auto src = static_cast<const value_type*>(buffer);
// Overwrite existing part
const u64 overlap = std::min<u64>(obj.size() - pos, size);
std::copy(src, src + overlap, obj.begin() + pos);
// Append new data
obj.insert(obj.end(), src + overlap, src + size);
pos += size;
return size;
}
u64 seek(s64 offset, seek_mode whence) override
{
return
whence == fs::seek_set ? pos = offset :
whence == fs::seek_cur ? pos = offset + pos :
whence == fs::seek_end ? pos = offset + size() :
(fmt::raw_error("fs::container_stream<>::seek(): invalid whence"), 0);
}
u64 size() override
{
return obj.size();
}
};
template <typename T>
file make_stream(T&& container = T{})
{
file result;
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container)));
return result;
}
}
+110 -45
View File
@@ -68,16 +68,16 @@ static u8* s_unwind_info;
static u64 s_unwind_size;
#ifdef _WIN32
static std::vector<RUNTIME_FUNCTION> s_unwind; // Custom .pdata section replacement
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
#endif
// Helper class
struct MemoryManager final : llvm::RTDyldMemoryManager
{
std::unordered_map<std::string, std::uintptr_t> table;
std::unordered_map<std::string, std::uintptr_t>& m_link;
MemoryManager(std::unordered_map<std::string, std::uintptr_t>&& table)
: table(std::move(table))
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
: m_link(table)
{
}
@@ -88,22 +88,22 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
virtual u64 getSymbolAddress(const std::string& name) override
{
if (u64 addr = RTDyldMemoryManager::getSymbolAddress(name))
{
// This may be bad if LLVM requests some built-in functions like fma.
LOG_ERROR(GENERAL, "LLVM: Symbol requested %s -> 0x%016llx", name, addr);
return addr;
}
const auto found = m_link.find(name);
const auto found = table.find(name);
if (found != table.end())
if (found != m_link.end())
{
return found->second;
}
if (u64 addr = RTDyldMemoryManager::getSymbolAddress(name))
{
// This may be bad if LLVM requests some built-in functions like fma.
LOG_ERROR(GENERAL, "LLVM: Symbol requested: %s -> 0x%016llx", name, addr);
return addr;
}
// It's fine if some function is never called, for example.
LOG_ERROR(GENERAL, "LLVM: Linkage failed for %s", name);
LOG_ERROR(GENERAL, "LLVM: Symbol not found: %s", name);
return (u64)null;
}
@@ -131,7 +131,7 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
s_code_addr = (u8*)m_next;
s_code_size = size;
LOG_SUCCESS(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), m_next, size, align);
LOG_NOTICE(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), m_next, size, align);
return (u8*)std::exchange(m_next, (void*)next);
}
@@ -161,21 +161,21 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
return nullptr;
}
LOG_SUCCESS(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), m_next, size, align, is_ro ? "ro" : "rw");
LOG_NOTICE(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), m_next, size, align, is_ro ? "ro" : "rw");
return (u8*)std::exchange(m_next, (void*)next);
}
virtual bool finalizeMemory(std::string* = nullptr) override
{
// TODO: make only read-only sections read-only
#ifdef _WIN32
DWORD op;
VirtualProtect(s_memory, (u64)m_next - (u64)s_memory, PAGE_READONLY, &op);
VirtualProtect(s_code_addr, s_code_size, PAGE_EXECUTE_READ, &op);
#else
::mprotect(s_memory, (u64)m_next - (u64)s_memory, PROT_READ);
::mprotect(s_code_addr, s_code_size, PROT_READ | PROT_EXEC);
#endif
//#ifdef _WIN32
// DWORD op;
// VirtualProtect(s_memory, (u64)m_next - (u64)s_memory, PAGE_READONLY, &op);
// VirtualProtect(s_code_addr, s_code_size, PAGE_EXECUTE_READ, &op);
//#else
// ::mprotect(s_memory, (u64)m_next - (u64)s_memory, PROT_READ);
// ::mprotect(s_code_addr, s_code_size, PROT_READ | PROT_EXEC);
//#endif
return false;
}
@@ -197,19 +197,24 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
~MemoryManager()
{
#ifdef _WIN32
if (!RtlDeleteFunctionTable(s_unwind.data()))
for (auto&& unwind : s_unwind)
{
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable(%p) failed! Error %u", s_unwind_info, GetLastError());
if (!RtlDeleteFunctionTable(unwind.data()))
{
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable() failed! Error %u", GetLastError());
}
}
s_unwind.clear();
if (!VirtualFree(s_memory, 0, MEM_DECOMMIT))
{
LOG_FATAL(GENERAL, "VirtualFree(%p) failed! Error %u", s_memory, GetLastError());
}
#else
if (::mprotect(s_memory, s_memory_size, PROT_NONE))
if (!::mmap(s_memory, s_memory_size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0))
{
LOG_FATAL(GENERAL, "mprotect(%p) failed! Error %d", s_memory, errno);
LOG_FATAL(GENERAL, "mmap(%p) failed! Error %d", s_memory, errno);
}
// TODO: unregister EH frames if necessary
@@ -223,36 +228,52 @@ private:
// Helper class
struct EventListener final : llvm::JITEventListener
{
std::string path;
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
{
const llvm::StringRef elf = obj.getData();
fs::file(fs::get_config_dir() + "LLVM.obj", fs::rewrite)
.write(elf.data(), elf.size());
if (!path.empty())
{
const llvm::StringRef elf = obj.getData();
fs::file(path, fs::rewrite).write(elf.data(), elf.size());
}
}
};
static EventListener s_listener;
jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unordered_map<std::string, std::uintptr_t>&& table)
static void dummy()
{
}
jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_linkage_info)
: m_link(std::move(init_linkage_info))
{
#ifdef _MSC_VER
m_link.emplace("__chkstk", (u64)&dummy);
#endif
verify(HERE), s_memory;
std::string result;
const auto module_ptr = _module.get();
// Initialization
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
LLVMLinkInMCJIT();
const auto _cpu = llvm::sys::getHostCPUName();
m_cpu = llvm::sys::getHostCPUName();
m_engine.reset(llvm::EngineBuilder(std::move(_module))
if (m_cpu == "skylake")
{
m_cpu = "haswell";
}
std::string result;
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("", g_llvm_ctx))
.setErrorStr(&result)
.setMCJITMemoryManager(std::make_unique<MemoryManager>(std::move(table)))
.setMCJITMemoryManager(std::make_unique<MemoryManager>(m_link))
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setCodeModel((u64)s_memory <= 0x60000000 ? llvm::CodeModel::Small : llvm::CodeModel::Large) // TODO
.setMCPU(_cpu == "skylake" ? "haswell" : _cpu)
.setMCPU(m_cpu)
.create());
if (!m_engine)
@@ -262,8 +283,45 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
m_engine->setProcessAllSections(true); // ???
m_engine->RegisterJITEventListener(&s_listener);
}
void jit_compiler::load(std::unique_ptr<llvm::Module> module, std::unique_ptr<llvm::object::ObjectFile> object)
{
s_listener.path.clear();
auto* module_ptr = module.get();
m_engine->addModule(std::move(module));
m_engine->addObjectFile(std::move(object));
m_engine->finalizeObject();
m_map.clear();
for (auto& func : module_ptr->functions())
{
const std::string& name = func.getName();
if (!m_link.count(name))
{
// Register compiled function
m_map[name] = m_engine->getFunctionAddress(name);
}
}
init();
}
void jit_compiler::make(std::unique_ptr<llvm::Module> module, std::string path)
{
s_listener.path = std::move(path);
auto* module_ptr = module.get();
m_engine->addModule(std::move(module));
m_engine->finalizeObject();
m_map.clear();
for (auto& func : module_ptr->functions())
{
if (!func.empty())
@@ -278,6 +336,11 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
func.deleteBody();
}
init();
}
void jit_compiler::init()
{
#ifdef _WIN32
// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
std::set<u64> func_set;
@@ -290,8 +353,8 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
const u64 base = (u64)s_memory;
const u8* bits = s_unwind_info;
s_unwind.clear();
s_unwind.reserve(m_map.size());
std::vector<RUNTIME_FUNCTION> unwind;
unwind.reserve(m_map.size());
for (const u64 addr : func_set)
{
@@ -304,7 +367,7 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
uw.BeginAddress = static_cast<u32>(addr - base);
uw.EndAddress = static_cast<u32>(next - base);
uw.UnwindData = static_cast<u32>((u64)bits - base);
s_unwind.emplace_back(uw);
unwind.emplace_back(uw);
// Parse .xdata UNWIND_INFO record
const u8 flags = *bits++; // Version and flags
@@ -327,14 +390,16 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
{
LOG_ERROR(GENERAL, "LLVM: .xdata analysis failed! (%p != %p)", s_unwind_info + s_unwind_size, bits);
}
else if (!RtlAddFunctionTable(s_unwind.data(), (DWORD)s_unwind.size(), base))
else if (!RtlAddFunctionTable(unwind.data(), (DWORD)unwind.size(), base))
{
LOG_ERROR(GENERAL, "RtlAddFunctionTable(%p) failed! Error %u", s_unwind_info, GetLastError());
}
else
{
LOG_SUCCESS(GENERAL, "LLVM: UNWIND_INFO registered (%p, size=0x%llx)", s_unwind_info, s_unwind_size);
LOG_NOTICE(GENERAL, "LLVM: UNWIND_INFO registered (%p, size=0x%llx)", s_unwind_info, s_unwind_size);
}
s_unwind.emplace_back(std::move(unwind));
#endif
}
+24 -1
View File
@@ -8,6 +8,7 @@
#include "types.h"
#include "restore_new.h"
#ifdef _MSC_VER
#pragma warning(push, 0)
#endif
@@ -17,6 +18,7 @@
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "define_new_memleakdetect.h"
extern llvm::LLVMContext g_llvm_ctx;
@@ -29,10 +31,25 @@ class jit_compiler final
// Compiled functions
std::unordered_map<std::string, std::uintptr_t> m_map;
// Linkage cache
std::unordered_map<std::string, std::uintptr_t> m_link;
// Arch
std::string m_cpu;
// Internal
void init();
public:
jit_compiler(std::unique_ptr<llvm::Module>&&, std::unordered_map<std::string, std::uintptr_t>&&);
jit_compiler(std::unordered_map<std::string, std::uintptr_t>);
~jit_compiler();
// Compile module
void make(std::unique_ptr<llvm::Module>, std::string);
// Load object
void load(std::unique_ptr<llvm::Module>, std::unique_ptr<llvm::object::ObjectFile>);
// Get compiled function address
std::uintptr_t get(const std::string& name) const
{
@@ -45,6 +62,12 @@ public:
return 0;
}
// Get CPU info
const std::string& cpu() const
{
return m_cpu;
}
};
#endif
+55 -4
View File
@@ -5,6 +5,12 @@
#include "rpcs3_version.h"
#include <string>
#ifdef _WIN32
#include <Windows.h>
#else
#include <chrono>
#endif
// Thread-specific log prefix provider
thread_local std::string(*g_tls_log_prefix)() = nullptr;
@@ -56,7 +62,7 @@ namespace logs
}
// Encode level, current thread name, channel name and write log message
virtual void log(const message& msg, const std::string& prefix, const std::string& text) override;
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) override;
};
static file_listener* get_logger()
@@ -66,6 +72,39 @@ namespace logs
return &logger;
}
static u64 get_stamp()
{
static struct time_initializer
{
#ifdef _WIN32
LARGE_INTEGER freq;
LARGE_INTEGER start;
time_initializer()
{
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
}
#else
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
#endif
u64 get() const
{
#ifdef _WIN32
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
const LONGLONG diff = now.QuadPart - start.QuadPart;
return diff / freq.QuadPart * 1'000'000 + diff % freq.QuadPart * 1'000'000 / freq.QuadPart;
#else
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
#endif
}
} timebase{};
return timebase.get();
}
channel GENERAL(nullptr, level::notice);
channel LOADER("LDR", level::notice);
channel MEMORY("MEM", level::notice);
@@ -76,6 +115,10 @@ namespace logs
channel ARMv7("ARMv7");
}
logs::listener::~listener()
{
}
void logs::listener::add(logs::listener* _new)
{
// Get first (main) listener
@@ -90,6 +133,9 @@ void logs::listener::add(logs::listener* _new)
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
{
// Get timestamp
const u64 stamp = get_stamp();
std::string text; fmt::raw_append(text, fmt, sup, args);
std::string prefix(g_tls_log_prefix ? g_tls_log_prefix() : "");
@@ -99,7 +145,7 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
// Send message to all listeners
while (lis)
{
lis->log(*this, prefix, text);
lis->log(stamp, *this, prefix, text);
lis = lis->m_next;
}
}
@@ -126,7 +172,7 @@ void logs::file_writer::log(const char* text, std::size_t size)
m_file.write(text, size);
}
void logs::file_listener::log(const logs::message& msg, const std::string& prefix, const std::string& _text)
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
{
std::string text; text.reserve(prefix.size() + _text.size() + 200);
@@ -143,7 +189,12 @@ void logs::file_listener::log(const logs::message& msg, const std::string& prefi
case level::trace: text = u8"·T "; break;
}
// TODO: print time?
// Print miscosecond timestamp
const u64 hours = stamp / 3600'000'000;
const u64 mins = (stamp % 3600'000'000) / 60'000'000;
const u64 secs = (stamp % 60'000'000) / 1'000'000;
const u64 frac = (stamp % 1'000'000);
fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac);
if (prefix.size() > 0)
{
+2 -2
View File
@@ -40,10 +40,10 @@ namespace logs
public:
constexpr listener() = default;
virtual ~listener() = default;
virtual ~listener();
// Process log message
virtual void log(const message& msg, const std::string& prefix, const std::string& text) = 0;
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) = 0;
// Add new listener
static void add(listener*);
-90
View File
@@ -1,90 +0,0 @@
#include "Utilities/Semaphore.h"
#include <mutex>
#include <condition_variable>
struct benaphore::internal
{
std::mutex mutex;
std::size_t acq_order{};
std::size_t rel_order{};
std::condition_variable cond;
};
void benaphore::wait_hard()
{
initialize_once();
std::unique_lock<std::mutex> lock(m_data->mutex);
// Notify non-zero waiter queue size
if (m_value.exchange(-1) == 1)
{
// Return immediately (acquired)
m_value = 0;
return;
}
// Remember the order
const std::size_t order = ++m_data->acq_order;
// Wait for the appropriate rel_order (TODO)
while (m_data->rel_order < order)
{
m_data->cond.wait(lock);
}
if (order == m_data->acq_order && m_data->acq_order == m_data->rel_order)
{
// Cleaup
m_data->acq_order = 0;
m_data->rel_order = 0;
m_value.compare_and_swap(-1, 0);
}
}
void benaphore::post_hard()
{
initialize_once();
std::unique_lock<std::mutex> lock(m_data->mutex);
if (m_value.compare_and_swap(0, 1) != -1)
{
// Do nothing (released)
return;
}
if (m_data->acq_order == m_data->rel_order)
{
m_value = 1;
return;
}
// Awake one thread
m_data->rel_order += 1;
// Unlock and notify
lock.unlock();
m_data->cond.notify_one();
}
void benaphore::initialize_once()
{
if (UNLIKELY(!m_data))
{
auto ptr = new benaphore::internal;
if (!m_data.compare_and_swap_test(nullptr, ptr))
{
delete ptr;
}
}
}
benaphore::~benaphore()
{
delete m_data;
}
-47
View File
@@ -1,47 +0,0 @@
#pragma once
#include "types.h"
#include "Atomic.h"
// Binary semaphore
class benaphore
{
struct internal;
// Reserved value (-1) enforces *_hard() calls
atomic_t<u32> m_value{};
atomic_t<internal*> m_data{};
void wait_hard();
void post_hard();
public:
constexpr benaphore() = default;
~benaphore();
// Initialize internal data
void initialize_once();
void wait()
{
if (UNLIKELY(!m_value.compare_and_swap_test(1, 0)))
{
wait_hard();
}
}
bool try_wait()
{
return m_value.compare_and_swap_test(1, 0);
}
void post()
{
if (UNLIKELY(!m_value.compare_and_swap_test(0, 1)))
{
post_hard();
}
}
};
-187
View File
@@ -1,187 +0,0 @@
#include "SharedMutex.h"
#include <mutex>
#include <condition_variable>
struct shared_mutex::internal
{
std::mutex mutex;
std::size_t rq_size{}; // Reader queue size (threads waiting on m_rcv)
std::size_t wq_size{}; // Writer queue size (threads waiting on m_wcv and m_ocv)
std::condition_variable rcv; // Reader queue
std::condition_variable wcv; // Writer queue
std::condition_variable ocv; // For current exclusive owner
};
void shared_mutex::lock_shared_hard()
{
initialize_once();
std::unique_lock<std::mutex> lock(m_data->mutex);
// Validate
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock_shared(): Invalid bit");
if ((m_ctrl & SM_READER_MASK) == 0) throw std::runtime_error("shared_mutex::lock_shared(): No readers");
// Notify non-zero reader queue size
m_ctrl |= SM_WAITERS_BIT, m_data->rq_size++;
// Fix excess reader count
if ((--m_ctrl & SM_READER_MASK) == 0 && m_data->wq_size)
{
// Notify exclusive owner
m_data->ocv.notify_one();
}
// Obtain the reader lock
while (true)
{
const auto ctrl = m_ctrl.load();
// Check writers and reader limit
if (m_data->wq_size || (ctrl & ~SM_WAITERS_BIT) >= SM_READER_MAX)
{
m_data->rcv.wait(lock);
continue;
}
if (m_ctrl.compare_and_swap_test(ctrl, ctrl + 1))
{
break;
}
}
if (!--m_data->rq_size && !m_data->wq_size)
{
m_ctrl &= ~SM_WAITERS_BIT;
}
}
void shared_mutex::unlock_shared_notify()
{
initialize_once();
std::unique_lock<std::mutex> lock(m_data->mutex);
if ((m_ctrl & SM_READER_MASK) == 0 && m_data->wq_size)
{
// Notify exclusive owner
lock.unlock();
m_data->ocv.notify_one();
}
else if (m_data->rq_size)
{
// Notify other readers
lock.unlock();
m_data->rcv.notify_one();
}
}
void shared_mutex::lock_hard()
{
initialize_once();
std::unique_lock<std::mutex> lock(m_data->mutex);
// Validate
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock(): Invalid bit");
// Notify non-zero writer queue size
m_ctrl |= SM_WAITERS_BIT, m_data->wq_size++;
// Obtain the writer lock
while (true)
{
const auto ctrl = m_ctrl.load();
if (ctrl & SM_WRITER_LOCK)
{
m_data->wcv.wait(lock);
continue;
}
if (m_ctrl.compare_and_swap_test(ctrl, ctrl | SM_WRITER_LOCK))
{
break;
}
}
// Wait for remaining readers
while ((m_ctrl & SM_READER_MASK) != 0)
{
m_data->ocv.wait(lock);
}
if (!--m_data->wq_size && !m_data->rq_size)
{
m_ctrl &= ~SM_WAITERS_BIT;
}
}
void shared_mutex::unlock_notify()
{
initialize_once();
std::unique_lock<std::mutex> lock(m_data->mutex);
if (m_data->wq_size)
{
// Notify next exclusive owner
lock.unlock();
m_data->wcv.notify_one();
}
else if (m_data->rq_size)
{
// Notify all readers
lock.unlock();
m_data->rcv.notify_all();
}
}
void shared_mutex::lock_upgrade_hard()
{
unlock_shared();
lock();
}
void shared_mutex::lock_degrade_hard()
{
initialize_once();
std::unique_lock<std::mutex> lock(m_data->mutex);
m_ctrl -= SM_WRITER_LOCK - 1;
if (m_data->rq_size)
{
// Notify all readers
lock.unlock();
m_data->rcv.notify_all();
}
else if (m_data->wq_size)
{
// Notify next exclusive owner
lock.unlock();
m_data->wcv.notify_one();
}
}
void shared_mutex::initialize_once()
{
if (UNLIKELY(!m_data))
{
auto ptr = new shared_mutex::internal;
if (!m_data.compare_and_swap_test(nullptr, ptr))
{
delete ptr;
}
}
}
shared_mutex::~shared_mutex()
{
delete m_data;
}
-179
View File
@@ -1,179 +0,0 @@
#pragma once
#include "types.h"
#include "Atomic.h"
//! An attempt to create effective implementation of "shared mutex", lock-free in optimistic case.
//! All locking and unlocking may be done by a single LOCK XADD or LOCK CMPXCHG instruction.
//! MSVC implementation of std::shared_timed_mutex seems suboptimal.
//! std::shared_mutex is not available until C++17.
class shared_mutex final
{
enum : u32
{
SM_WRITER_LOCK = 1u << 31, // Exclusive lock flag, must be MSB
SM_WAITERS_BIT = 1u << 30, // Flag set if m_wq_size or m_rq_size is non-zero
SM_INVALID_BIT = 1u << 29, // Unreachable reader count bit (may be set by incorrect unlock_shared() call)
SM_READER_MASK = SM_WAITERS_BIT - 1, // Valid reader count bit mask
SM_READER_MAX = 1u << 24, // Max reader count
};
atomic_t<u32> m_ctrl{}; // Control variable: reader count | SM_* flags
struct internal;
atomic_t<internal*> m_data{}; // Internal data
void lock_shared_hard();
void unlock_shared_notify();
void lock_hard();
void unlock_notify();
void lock_upgrade_hard();
void lock_degrade_hard();
public:
constexpr shared_mutex() = default;
// Initialize internal data
void initialize_once();
~shared_mutex();
bool try_lock_shared()
{
const u32 ctrl = m_ctrl.load();
return ctrl < SM_READER_MAX && m_ctrl.compare_and_swap_test(ctrl, ctrl + 1);
}
void lock_shared()
{
// Optimization: unconditional increment, compensated later
if (UNLIKELY(m_ctrl++ >= SM_READER_MAX))
{
lock_shared_hard();
}
}
void unlock_shared()
{
if (UNLIKELY(m_ctrl-- >= SM_READER_MAX))
{
unlock_shared_notify();
}
}
bool try_lock()
{
return !m_ctrl && m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK);
}
void lock()
{
if (UNLIKELY(!m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK)))
{
lock_hard();
}
}
void unlock()
{
m_ctrl &= ~SM_WRITER_LOCK;
if (UNLIKELY(m_ctrl))
{
unlock_notify();
}
}
bool try_lock_upgrade()
{
return m_ctrl == 1 && m_ctrl.compare_and_swap_test(1, SM_WRITER_LOCK);
}
bool try_lock_degrade()
{
return m_ctrl == SM_WRITER_LOCK && m_ctrl.compare_and_swap_test(SM_WRITER_LOCK, 1);
}
void lock_upgrade()
{
if (UNLIKELY(!m_ctrl.compare_and_swap_test(1, SM_WRITER_LOCK)))
{
lock_upgrade_hard();
}
}
void lock_degrade()
{
if (UNLIKELY(!m_ctrl.compare_and_swap_test(SM_WRITER_LOCK, 1)))
{
lock_degrade_hard();
}
}
};
//! Simplified shared (reader) lock implementation.
//! std::shared_lock may be used instead if necessary.
class reader_lock final
{
shared_mutex& m_mutex;
public:
reader_lock(const reader_lock&) = delete;
reader_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock_shared();
}
~reader_lock()
{
m_mutex.unlock_shared();
}
};
//! Simplified exclusive (writer) lock implementation.
//! std::lock_guard may or std::unique_lock be used instead if necessary.
class writer_lock final
{
shared_mutex& m_mutex;
public:
writer_lock(const writer_lock&) = delete;
writer_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock();
}
~writer_lock()
{
m_mutex.unlock();
}
};
// Exclusive (writer) lock in the scope of shared (reader) lock.
class upgraded_lock final
{
shared_mutex& m_mutex;
public:
upgraded_lock(const writer_lock&) = delete;
upgraded_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock_upgrade();
}
~upgraded_lock()
{
m_mutex.lock_degrade();
}
};
-81
View File
@@ -1,81 +0,0 @@
#pragma once
#include <deque>
// Tag used in sleep_entry<> constructor
static struct defer_sleep_tag {} constexpr defer_sleep{};
// Define sleep queue as std::deque with T* pointers, T - thread type
template<typename T> using sleep_queue = std::deque<T*>;
// Automatic object handling a thread pointer (T*) in the sleep queue
// Sleep queue is actually std::deque with pointers, be careful about the lifetime
template<typename T>
class sleep_entry final
{
sleep_queue<T>& m_queue;
T& m_thread;
public:
// Constructor; enter() not called
sleep_entry(sleep_queue<T>& queue, T& entry, const defer_sleep_tag&)
: m_queue(queue)
, m_thread(entry)
{
}
// Constructor; calls enter()
sleep_entry(sleep_queue<T>& queue, T& entry)
: sleep_entry(queue, entry, defer_sleep)
{
enter();
}
// Destructor; calls leave()
~sleep_entry()
{
leave();
}
// Add thread to the sleep queue
void enter()
{
for (auto t : m_queue)
{
if (t == &m_thread)
{
// Already exists, is it an error?
return;
}
}
m_queue.emplace_back(&m_thread);
}
// Remove thread from the sleep queue
void leave()
{
for (auto it = m_queue.begin(), end = m_queue.end(); it != end; it++)
{
if (*it == &m_thread)
{
m_queue.erase(it);
return;
}
}
}
// Check whether the thread exists in the sleep queue
explicit operator bool() const
{
for (auto it = m_queue.begin(), end = m_queue.end(); it != end; it++)
{
if (*it == &m_thread)
{
return true;
}
}
return false;
}
};
+15 -3
View File
@@ -11,6 +11,18 @@
#include <errno.h>
#endif
template <>
void fmt_class_string<std::pair<const fmt_type_info*, u64>>::format(std::string& out, u64 arg)
{
// Dynamic format arg
const auto& pair = get_object(arg);
if (pair.first)
{
pair.first->fmt_string(out, pair.second);
}
}
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
{
if (arg)
@@ -154,12 +166,12 @@ namespace fmt
#ifdef _WIN32
if (DWORD error = GetLastError())
{
fmt::append(out, " (e%#x)", error);
fmt::append(out, " (e=%#x)", error);
}
#else
if (int error = errno)
{
fmt::append(out, " (e%d)", error);
fmt::append(out, " (e=%d)", error);
}
#endif
@@ -240,7 +252,7 @@ struct fmt::cfmt_src
template <typename T>
T get(std::size_t index) const
{
return reinterpret_cast<const T&>(args[index]);
return *reinterpret_cast<const T*>(reinterpret_cast<const u8*>(args + index));
}
void skip(std::size_t extra)
+9 -4
View File
@@ -60,9 +60,9 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
using type = T;
// Convert FP to f64 and reinterpret (TODO?)
static inline u64 get(f64 arg)
static inline u64 get(const f64 arg)
{
return reinterpret_cast<u64&>(arg);
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
}
};
@@ -153,7 +153,7 @@ struct fmt_class_string
out += prefix;
for (u64 i = 0; i < 64; i++)
for (u64 i = 0; i < 63; i++)
{
const u64 mask = 1ull << i;
@@ -161,13 +161,18 @@ struct fmt_class_string
{
fmt(out, i);
if (arg > mask)
if (arg >> (i + 1))
{
out += delim;
}
}
}
if (arg & (1ull << 63))
{
fmt(out, 63);
}
out += suffix;
}
+229 -839
View File
File diff suppressed because it is too large Load Diff
+79 -267
View File
@@ -7,6 +7,9 @@
#include <string>
#include <memory>
#include "sema.h"
#include "cond.h"
// Will report exception and call std::abort() if put in catch(...)
[[noreturn]] void catch_all_exceptions();
@@ -17,19 +20,19 @@ class task_stack
{
std::unique_ptr<task_base> next;
virtual ~task_base() = default;
virtual ~task_base();
virtual void exec()
virtual void invoke()
{
if (next)
{
next->exec();
next->invoke();
}
}
};
template<typename F>
struct task_type : task_base
template <typename F>
struct task_type final : task_base
{
std::remove_reference_t<F> func;
@@ -38,10 +41,10 @@ class task_stack
{
}
void exec() override
void invoke() final override
{
func();
task_base::exec();
task_base::invoke();
}
};
@@ -50,7 +53,7 @@ class task_stack
public:
task_stack() = default;
template<typename F>
template <typename F>
task_stack(F&& func)
: m_stack(new task_type<F>(std::forward<F>(func)))
{
@@ -70,11 +73,11 @@ public:
m_stack.reset();
}
void exec() const
void invoke() const
{
if (m_stack)
{
m_stack->exec();
m_stack->invoke();
}
}
};
@@ -82,23 +85,32 @@ public:
// Thread control class
class thread_ctrl final
{
public: // TODO
struct internal;
private:
// Current thread
static thread_local thread_ctrl* g_tls_this_thread;
// Thread handle storage
std::aligned_storage_t<16> m_thread;
// Self pointer
std::shared_ptr<thread_ctrl> m_self;
// Thread join contention counter
atomic_t<u32> m_joining{};
// Thread handle (platform-specific)
atomic_t<std::uintptr_t> m_thread{0};
// Thread interrupt guard counter
volatile u32 m_guard = 0x80000000;
// Thread mutex
mutable semaphore<> m_mutex;
// Thread internals
atomic_t<internal*> m_data{};
// Thread condition variable
cond_variable m_cond;
// Thread flags
atomic_t<u32> m_signal{0};
// Thread joining condition variable
cond_variable m_jcv;
// Remotely set or caught exception
std::exception_ptr m_exception;
// Thread initial task or atexit task
task_stack m_task;
// Fixed name
std::string m_name;
@@ -110,19 +122,19 @@ private:
void initialize();
// Called at the thread end
void finalize() noexcept;
void finalize(std::exception_ptr) noexcept;
// Get atexit function
void push_atexit(task_stack);
// Add task (atexit)
static void _push(task_stack);
// Start waiting
void wait_start(u64 timeout);
// Internal waiting function, may throw. Infinite value is -1.
static bool _wait_for(u64 usec);
// Proceed waiting
bool wait_wait(u64 timeout);
// Internal throwing function. Mutex must be locked and will be unlocked.
[[noreturn]] void _throw();
// Check exception
void test();
// Internal notification function
void _notify(cond_variable thread_ctrl::*);
public:
thread_ctrl(std::string&& name);
@@ -137,179 +149,57 @@ public:
return m_name;
}
// Initialize internal data
void initialize_once();
// Get exception
std::exception_ptr get_exception() const;
// Set exception
void set_exception(std::exception_ptr ptr);
// Get thread result (may throw, simultaneous joining allowed)
void join();
// Lock thread mutex
void lock();
// Lock conditionally (double-checked)
template<typename F>
bool lock_if(F&& pred)
{
if (pred())
{
lock();
try
{
if (LIKELY(pred()))
{
return true;
}
else
{
unlock();
return false;
}
}
catch (...)
{
unlock();
throw;
}
}
else
{
return false;
}
}
// Unlock thread mutex (internal data must be initialized)
void unlock();
// Lock, unlock, notify the thread (required if the condition changed locklessly)
void lock_notify();
// Notify the thread (internal data must be initialized)
// Notify the thread
void notify();
// Set exception (internal data must be initialized, thread mutex must be locked)
void set_exception(std::exception_ptr);
// Internal
static void handle_interrupt();
// Interrupt thread with specified handler call (thread mutex must be locked)
void interrupt(void(*handler)());
// Interrupt guard recursive enter
void guard_enter()
// Wait once with timeout. Abortable, may throw. May spuriously return false.
static inline bool wait_for(u64 usec)
{
m_guard++;
return _wait_for(usec);
}
// Interrupt guard recursive leave
void guard_leave()
{
if (UNLIKELY(--m_guard & 0x40000000))
{
test_interrupt();
}
}
// Allow interrupts
void interrupt_enable()
{
m_guard &= ~0x80000000;
}
// Disable and discard any interrupt
void interrupt_disable()
{
m_guard |= 0x80000000;
}
// Check interrupt if delayed by guard scope
void test_interrupt();
// Current thread sleeps for specified amount of microseconds.
// Wrapper for std::this_thread::sleep, doesn't require valid thread_ctrl.
[[deprecated]] static void sleep(u64 useconds);
// Wait until pred(). Abortable, may throw. Thread must be locked.
// Timeout in microseconds (zero means infinite).
template<typename F>
static inline auto wait_for(u64 useconds, F&& pred)
{
if (useconds)
{
g_tls_this_thread->wait_start(useconds);
}
while (true)
{
g_tls_this_thread->test();
if (auto&& result = pred())
{
return result;
}
else if (!g_tls_this_thread->wait_wait(useconds) && useconds)
{
return result;
}
}
}
// Wait once. Abortable, may throw. Thread must be locked.
// Timeout in microseconds (zero means infinite).
static inline bool wait_for(u64 useconds = 0)
{
if (useconds)
{
g_tls_this_thread->wait_start(useconds);
}
g_tls_this_thread->test();
if (!g_tls_this_thread->wait_wait(useconds) && useconds)
{
return false;
}
g_tls_this_thread->test();
return true;
}
// Wait until pred(). Abortable, may throw. Thread must be locked.
template<typename F>
static inline auto wait(F&& pred)
{
while (true)
{
g_tls_this_thread->test();
if (auto&& result = pred())
{
return result;
}
g_tls_this_thread->wait_wait(0);
}
}
// Wait once. Abortable, may throw. Thread must be locked.
// Wait. Abortable, may throw.
static inline void wait()
{
g_tls_this_thread->test();
g_tls_this_thread->wait_wait(0);
g_tls_this_thread->test();
_wait_for(-1);
}
// Wait eternally. Abortable, may throw. Thread must be locked.
// Wait until pred(). Abortable, may throw.
template<typename F, typename RT = std::result_of_t<F()>>
static inline RT wait(F&& pred)
{
while (true)
{
if (RT result = pred())
{
return result;
}
_wait_for(-1);
}
}
// Wait eternally until aborted.
[[noreturn]] static inline void eternalize()
{
while (true)
{
g_tls_this_thread->test();
g_tls_this_thread->wait_wait(0);
_wait_for(-1);
}
}
// Test exception (may throw).
static void test();
// Get current thread (may be nullptr)
static thread_ctrl* get_current()
{
@@ -320,14 +210,14 @@ public:
template<typename F>
static inline void atexit(F&& func)
{
return g_tls_this_thread->push_atexit(std::forward<F>(func));
_push(std::forward<F>(func));
}
// Named thread factory
// Create detached named thread
template<typename N, typename F>
static inline void spawn(N&& name, F&& func)
{
auto&& out = std::make_shared<thread_ctrl>(std::forward<N>(name));
auto out = std::make_shared<thread_ctrl>(std::forward<N>(name));
thread_ctrl::start(out, std::forward<F>(func));
}
@@ -382,7 +272,7 @@ public:
}
// Access thread_ctrl
thread_ctrl* operator->() const
thread_ctrl* get() const
{
return m_thread.get();
}
@@ -392,90 +282,12 @@ public:
return m_thread->join();
}
void lock() const
{
return m_thread->lock();
}
void unlock() const
{
return m_thread->unlock();
}
void lock_notify() const
{
return m_thread->lock_notify();
}
void notify() const
{
return m_thread->notify();
}
};
// Simple thread mutex locker
class thread_lock final
{
thread_ctrl* m_thread;
public:
thread_lock(const thread_lock&) = delete;
// Lock specified thread
thread_lock(thread_ctrl* thread)
: m_thread(thread)
{
m_thread->lock();
}
// Lock specified named_thread
thread_lock(named_thread& thread)
: thread_lock(thread.operator->())
{
}
// Lock current thread
thread_lock()
: thread_lock(thread_ctrl::get_current())
{
}
~thread_lock()
{
m_thread->unlock();
}
};
// Interrupt guard scope
class thread_guard final
{
thread_ctrl* m_thread;
public:
thread_guard(const thread_guard&) = delete;
thread_guard(thread_ctrl* thread)
: m_thread(thread)
{
m_thread->guard_enter();
}
thread_guard(named_thread& thread)
: thread_guard(thread.operator->())
{
}
thread_guard()
: thread_guard(thread_ctrl::get_current())
{
}
~thread_guard() noexcept(false)
{
m_thread->guard_leave();
}
};
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
class scope_thread final
{
@@ -498,7 +310,7 @@ public:
}
// Access thread_ctrl
thread_ctrl* operator->() const
thread_ctrl* get() const
{
return m_thread.get();
}
+6 -6
View File
@@ -6,8 +6,8 @@ class Timer
{
private:
bool m_stopped;
std::chrono::high_resolution_clock::time_point m_start;
std::chrono::high_resolution_clock::time_point m_end;
std::chrono::steady_clock::time_point m_start;
std::chrono::steady_clock::time_point m_end;
public:
Timer() : m_stopped(false)
@@ -17,13 +17,13 @@ public:
void Start()
{
m_stopped = false;
m_start = std::chrono::high_resolution_clock::now();
m_start = std::chrono::steady_clock::now();
}
void Stop()
{
m_stopped = true;
m_end = std::chrono::high_resolution_clock::now();
m_end = std::chrono::steady_clock::now();
}
double GetElapsedTimeInSec() const
@@ -38,14 +38,14 @@ public:
u64 GetElapsedTimeInMicroSec() const
{
std::chrono::high_resolution_clock::time_point now = m_stopped ? m_end : std::chrono::high_resolution_clock::now();
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(now - m_start).count();
}
u64 GetElapsedTimeInNanoSec() const
{
std::chrono::high_resolution_clock::time_point now = m_stopped ? m_end : std::chrono::high_resolution_clock::now();
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - m_start).count();
}
+5 -5
View File
@@ -13,7 +13,7 @@
namespace memory_helper
{
void* reserve_memory(size_t size)
void* reserve_memory(std::size_t size)
{
#ifdef _WIN32
return verify("reserve_memory" HERE, VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS));
@@ -22,21 +22,21 @@ namespace memory_helper
#endif
}
void commit_page_memory(void* pointer, size_t size)
void commit_page_memory(void* pointer, std::size_t size)
{
#ifdef _WIN32
verify(HERE), VirtualAlloc(pointer, size, MEM_COMMIT, PAGE_READWRITE);
#else
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size, PROT_READ | PROT_WRITE) != -1;
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), PROT_READ | PROT_WRITE) != -1;
#endif
}
void free_reserved_memory(void* pointer, size_t size)
void free_reserved_memory(void* pointer, std::size_t size)
{
#ifdef _WIN32
verify(HERE), VirtualFree(pointer, 0, MEM_DECOMMIT);
#else
verify(HERE), ::mprotect(pointer, size, PROT_NONE) != -1;
verify(HERE), ::mmap(pointer, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
#endif
}
}
+1 -1
View File
@@ -581,7 +581,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
const std::string _fmt(fmt - ctx.size, fmt);
const u64 arg0 = src.template get<u64>(0);
const f64 arg0 = src.template get<f64>(0);
const u64 arg1 = ctx.args >= 1 ? src.template get<u64>(1) : 0;
const u64 arg2 = ctx.args >= 2 ? src.template get<u64>(2) : 0;
+105
View File
@@ -0,0 +1,105 @@
#include "cond.h"
#include "sync.h"
#ifndef _WIN32
#include <thread>
#endif
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
{
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
#ifdef _WIN32
LARGE_INTEGER timeout;
timeout.QuadPart = _timeout * -10;
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout))
{
verify(HERE), rc == WAIT_TIMEOUT;
// Retire
if (!m_value.fetch_op([](u32& value) { if (value) value--; }))
{
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
return true;
}
return false;
}
return true;
#else
timespec timeout;
timeout.tv_sec = _timeout / 1000000;
timeout.tv_nsec = (_timeout % 1000000) * 1000;
for (u32 value = _old + 1;; value = m_value)
{
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0
? 0
: errno;
// Normal or timeout wakeup
if (!err || (_timeout != -1 && err == ETIMEDOUT))
{
// Cleanup (remove waiter)
verify(HERE), m_value--;
return !err;
}
// Not a wakeup
verify(HERE), err == EAGAIN;
}
#endif
}
void cond_variable::imp_wake(u32 _count) noexcept
{
#ifdef _WIN32
// Try to subtract required amount of waiters
const u32 count = m_value.atomic_op([=](u32& value)
{
if (value > _count)
{
value -= _count;
return _count;
}
return std::exchange(value, 0);
});
for (u32 i = count; i > 0; i--)
{
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
}
#else
for (u32 i = _count; i > 0; std::this_thread::yield())
{
const u32 value = m_value;
// Constrain remaining amount with imaginary waiter count
if (i > value)
{
i = value;
}
if (!value || i == 0)
{
// Nothing to do
return;
}
if (const int res = futex((int*)&m_value.raw(), FUTEX_WAKE_PRIVATE, i > INT_MAX ? INT_MAX : i, nullptr, nullptr, 0))
{
verify(HERE), res >= 0 && (u32)res <= i;
i -= res;
}
if (!m_value || i == 0)
{
// Escape
return;
}
}
#endif
}
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include "types.h"
#include "Atomic.h"
// Lightweight condition variable
class cond_variable
{
// Internal waiter counter
atomic_t<u32> m_value{0};
protected:
// Internal waiting function
bool imp_wait(u32 _old, u64 _timeout) noexcept;
// Try to notify up to _count threads
void imp_wake(u32 _count) noexcept;
public:
constexpr cond_variable() = default;
// Intrusive wait algorithm for lockable objects
template <typename T>
explicit_bool_t wait(T& object, u64 usec_timeout = -1)
{
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
object.unlock();
const bool res = imp_wait(_old, usec_timeout);
object.lock();
return res;
}
// Wake one thread
void notify_one() noexcept
{
if (m_value)
{
imp_wake(1);
}
}
// Wake all threads
void notify_all() noexcept
{
if (m_value)
{
imp_wake(-1);
}
}
};
+9
View File
@@ -57,4 +57,13 @@ namespace utils
{
return loaded();
}
void* get_proc_address(const char* lib, const char* name)
{
#ifdef _WIN32
return reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA(lib), name));
#else
return dlsym(dlopen(lib, RTLD_NOLOAD), name);
#endif
}
}
+39
View File
@@ -38,4 +38,43 @@ namespace utils
bool loaded() const;
explicit operator bool() const;
};
// (assume the lib is always loaded)
void* get_proc_address(const char* lib, const char* name);
template <typename F>
struct dynamic_import
{
static_assert(sizeof(F) == 0, "Invalid function type");
};
template <typename R, typename... Args>
struct dynamic_import<R(Args...)>
{
R(*ptr)(Args...);
const char* const lib;
const char* const name;
// Constant initialization
constexpr dynamic_import(const char* lib, const char* name)
: ptr(nullptr)
, lib(lib)
, name(name)
{
}
// Caller
R operator()(Args... args)
{
if (!ptr)
{
// TODO: atomic
ptr = reinterpret_cast<R(*)(Args...)>(get_proc_address(lib, name));
}
return ptr(args...);
}
};
}
#define DYNAMIC_IMPORT(lib, name, ...) static utils::dynamic_import<__VA_ARGS__> name(lib, #name);
+185
View File
@@ -1,8 +1,193 @@
#pragma once
#include <functional>
#include <deque>
#include <list>
#include "Atomic.h"
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
T operator ++(T& value, int)
{
return std::exchange(value, static_cast<T>(value < T{} || value >= Mod ? static_cast<Under>(0) : static_cast<Under>(value) + 1));
}
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
T operator --(T& value, int)
{
return std::exchange(value, static_cast<T>(value <= T{} || value >= static_cast<Under>(Mod) - 1 ? static_cast<Under>(Mod) - 1 : static_cast<Under>(value) - 1));
}
template <typename T, typename CRT, std::size_t Size = static_cast<std::underlying_type_t<T>>(T::__state_enum_max)>
class state_machine
{
using under = std::underlying_type_t<T>;
using ftype = void(CRT::*)(T);
atomic_t<T> m_value;
template <std::size_t... Ind>
static inline ftype transition_map(std::integer_sequence<std::size_t, Ind...>, T state)
{
// Constantly initialized list of functions
static constexpr ftype map[Size]{&CRT::template transition<static_cast<T>(Ind)>...};
// Unsafe table lookup (TODO)
return map[static_cast<under>(state)];
}
// "Convert" variable argument to template argument
static inline ftype transition_get(T state)
{
return transition_map(std::make_index_sequence<Size>(), state);
}
public:
constexpr state_machine()
: m_value{T{}}
{
}
constexpr state_machine(T state)
: m_value{state}
{
}
// Get current state
T state_get() const
{
return m_value;
}
// Unconditionally set state
void state_set(T state)
{
T _old = m_value.exchange(state);
if (_old != state)
{
(static_cast<CRT*>(this)->*transition_get(state))(_old);
}
}
// Conditionally set state (optimized)
explicit_bool_t state_test_and_set(T expected, T state)
{
if (m_value == expected && m_value.compare_and_swap_test(expected, state))
{
(static_cast<CRT*>(this)->*transition_get(state))(expected);
return true;
}
return false;
}
// Conditionally set state (list version)
explicit_bool_t state_test_and_set(std::initializer_list<T> expected, T state)
{
T _old;
if (m_value.atomic_op([&](T& value)
{
for (T x : expected)
{
if (value == x)
{
_old = std::exchange(value, state);
return true;
}
}
return false;
}))
{
(static_cast<CRT*>(this)->*transition_get(state))(_old);
return true;
}
return false;
}
// Unconditionally set next state
void state_next()
{
T _old, state = m_value.op_fetch([&](T& value)
{
_old = value++;
});
(static_cast<CRT*>(this)->*transition_get(state))(_old);
}
// Unconditionally set previous state
void state_prev()
{
T _old, state = m_value.op_fetch([&](T& value)
{
_old = value--;
});
(static_cast<CRT*>(this)->*transition_get(state))(_old);
}
// Get number of states
static constexpr std::size_t size()
{
return Size;
}
};
//enum class test_state
//{
// on,
// off,
// la,
//
// __state_enum_max // 3
//};
//
//struct test_machine final : state_machine<test_state, test_machine>
//{
// template <test_state>
// void transition(test_state old_state);
//
// void on()
// {
// state_set(test_state::on);
// }
//
// void off()
// {
// state_set(test_state::off);
// }
//
// void test()
// {
// state_next();
// }
//};
//
//template <>
//void test_machine::transition<test_state::on>(test_state)
//{
// LOG_SUCCESS(GENERAL, "ON");
//}
//
//template <>
//void test_machine::transition<test_state::off>(test_state)
//{
// LOG_SUCCESS(GENERAL, "OFF");
//}
//
//
//template <>
//void test_machine::transition<test_state::la>(test_state)
//{
// on();
// off();
// test();
//}
enum class event_result
{
skip,
+186 -3
View File
@@ -66,9 +66,9 @@ public:
constexpr lf_fifo() = default;
// Get current "push" position
u32 size()
u32 size() const
{
return reinterpret_cast<atomic_t<u32>&>(m_ctrl).load(); // Hack
return reinterpret_cast<const atomic_t<u32>&>(m_ctrl).load(); // Hack
}
// Acquire the place for one or more elements.
@@ -78,7 +78,7 @@ public:
}
// Get current "pop" position
u32 peek()
u32 peek() const
{
return m_ctrl.load().pop;
}
@@ -145,3 +145,186 @@ public:
}
}
};
// Fixed-size single-producer single-consumer queue
template <typename T, std::uint32_t N>
class lf_spsc
{
// If N is a power of 2, m_push/m_pop can safely overflow and the algorithm is simplified
static_assert(N && (1u << 31) % N == 0, "lf_spsc<> error: size must be power of 2");
protected:
volatile std::uint32_t m_push{0};
volatile std::uint32_t m_pop{0};
T m_data[N]{};
public:
constexpr lf_spsc() = default;
// Try to push (producer only)
template <typename T2>
bool try_push(T2&& data)
{
const std::uint32_t pos = m_push;
if (pos - m_pop >= N)
{
return false;
}
_mm_lfence();
m_data[pos % N] = std::forward<T2>(data);
_mm_sfence();
m_push = pos + 1;
return true;
}
// Try to get push pointer (producer only)
operator T*()
{
const std::uint32_t pos = m_push;
if (pos - m_pop >= N)
{
return nullptr;
}
_mm_lfence();
return m_data + (pos % N);
}
// Increment push counter (producer only)
void end_push()
{
const std::uint32_t pos = m_push;
if (pos - m_pop < N)
{
_mm_sfence();
m_push = pos + 1;
}
}
// Unsafe access
T& get_push(std::size_t i)
{
_mm_lfence();
return m_data[(m_push + i) % N];
}
// Try to pop (consumer only)
template <typename T2>
bool try_pop(T2& out)
{
const std::uint32_t pos = m_pop;
if (m_push - pos <= 0)
{
return false;
}
_mm_lfence();
out = std::move(m_data[pos % N]);
_mm_sfence();
m_pop = pos + 1;
return true;
}
// Increment pop counter (consumer only)
void end_pop()
{
const std::uint32_t pos = m_pop;
if (m_push - pos > 0)
{
_mm_sfence();
m_pop = pos + 1;
}
}
// Get size (consumer only)
std::uint32_t size() const
{
return m_push - m_pop;
}
// Direct access (consumer only)
T& operator [](std::size_t i)
{
_mm_lfence();
return m_data[(m_pop + i) % N];
}
};
// Fixed-size multi-producer single-consumer queue
template <typename T, std::uint32_t N>
class lf_mpsc : lf_spsc<T, N>
{
protected:
using lf_spsc<T, N>::m_push;
using lf_spsc<T, N>::m_pop;
using lf_spsc<T, N>::m_data;
enum : std::uint64_t
{
c_ack = 1ull << 0,
c_rel = 1ull << 32,
};
atomic_t<std::uint64_t> m_lock{0};
void release(std::uint64_t value)
{
// Push all pending elements at once when possible
if (value && value % c_rel == value / c_rel)
{
_mm_sfence();
m_push += value % c_rel;
m_lock.compare_and_swap_test(value, 0);
}
}
public:
constexpr lf_mpsc() = default;
// Try to get push pointer
operator T*()
{
const std::uint64_t old = m_lock.fetch_add(c_ack);
const std::uint32_t pos = m_push;
if (old % N >= N || pos - m_pop >= N - (old % N))
{
release(m_lock.sub_fetch(c_ack));
return nullptr;
}
return m_data + ((pos + old) % N);
}
// Increment push counter (producer only)
void end_push()
{
release(m_lock.add_fetch(c_rel));
}
// Try to push
template <typename T2>
bool try_push(T2&& data)
{
if (T* ptr = *this)
{
*ptr = std::forward<T2>(data);
end_push();
return true;
}
return false;
}
// Enable consumer methods
using lf_spsc<T, N>::try_pop;
using lf_spsc<T, N>::end_pop;
using lf_spsc<T, N>::size;
using lf_spsc<T, N>::operator [];
};
+238
View File
@@ -0,0 +1,238 @@
#include "mutex.h"
#include "sync.h"
void shared_mutex::imp_lock_shared(s64 _old)
{
verify("shared_mutex overflow" HERE), _old <= c_max;
for (int i = 0; i < 10; i++)
{
busy_wait();
const s64 value = m_value.load();
if (value >= c_min && m_value.compare_and_swap_test(value, value - c_min))
{
return;
}
}
#ifdef _WIN32
// Acquire writer lock
imp_wait(m_value.load());
// Convert value
s64 value = m_value.fetch_add(c_one - c_min);
if (value != 0)
{
imp_unlock(value);
}
// Wait as a reader if necessary
if (value + c_one - c_min < 0)
{
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
}
#else
while (true)
{
const s64 value0 = m_value.fetch_op([](s64& value)
{
if (value >= c_min)
{
value -= c_min;
}
});
if (value0 >= c_min)
{
return;
}
// Acquire writer lock
imp_wait(value0);
// Convert to reader lock
s64 value1 = m_value.fetch_add(c_one - c_min);
if (value1 != 0)
{
imp_unlock(value1);
}
value1 += c_one - c_min;
if (value1 >= 0)
{
return;
}
// Wait as a reader if necessary
while (futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_PRIVATE, int(value1 >> 32), nullptr, nullptr, 0))
{
value1 = m_value.load();
if (value1 >= 0)
{
return;
}
}
// If blocked by writers, release the reader lock and try again
const s64 value2 = m_value.fetch_op([](s64& value)
{
if (value < 0)
{
value += c_min;
}
});
if (value2 >= 0)
{
return;
}
imp_unlock_shared(value2);
}
#endif
}
void shared_mutex::imp_unlock_shared(s64 _old)
{
verify("shared_mutex overflow" HERE), _old + c_min <= c_max;
// Check reader count, notify the writer if necessary
if ((_old + c_min) % c_one == 0)
{
#ifdef _WIN32
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
#else
m_value -= c_sig;
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
#endif
}
}
void shared_mutex::imp_wait(s64)
{
#ifdef _WIN32
if (m_value.sub_fetch(c_one))
{
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
}
#else
if (!m_value.sub_fetch(c_one))
{
// Return immediately if locked
return;
}
while (true)
{
// Load new value, try to acquire c_sig
const s64 value = m_value.fetch_op([](s64& value)
{
if (value <= c_one - c_sig)
{
value += c_sig;
}
});
if (value <= c_one - c_sig)
{
return;
}
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAIT_PRIVATE, int(value), nullptr, nullptr, 0);
}
#endif
}
void shared_mutex::imp_lock(s64 _old)
{
verify("shared_mutex overflow" HERE), _old <= c_max;
for (int i = 0; i < 10; i++)
{
busy_wait();
const s64 value = m_value.load();
if (value == c_one && m_value.compare_and_swap_test(c_one, 0))
{
return;
}
}
imp_wait(m_value.load());
}
void shared_mutex::imp_unlock(s64 _old)
{
verify("shared_mutex overflow" HERE), _old + c_one <= c_max;
// 1) Notify the next writer if necessary
// 2) Notify all readers otherwise if necessary
#ifdef _WIN32
if (_old + c_one <= 0)
{
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
}
else if (s64 count = -_old / c_min)
{
while (count--)
{
NtReleaseKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
}
}
#else
if (_old + c_one <= 0)
{
m_value -= c_sig;
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
}
else if (s64 count = -_old / c_min)
{
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
}
#endif
}
void shared_mutex::imp_lock_upgrade()
{
unlock_shared();
lock();
}
void shared_mutex::imp_lock_degrade()
{
unlock();
lock_shared();
}
bool shared_mutex::try_lock_shared()
{
// Conditional decrement
return m_value.fetch_op([](s64& value) { if (value >= c_min) value -= c_min; }) >= c_min;
}
bool shared_mutex::try_lock()
{
// Conditional decrement (TODO: obtain c_sig)
return m_value.compare_and_swap_test(c_one, 0);
}
bool shared_mutex::try_lock_upgrade()
{
// TODO
return m_value.compare_and_swap_test(c_one - c_min, 0);
}
bool shared_mutex::try_lock_degrade()
{
// TODO
return m_value.compare_and_swap_test(0, c_one - c_min);
}
+173
View File
@@ -0,0 +1,173 @@
#pragma once
#include "types.h"
#include "Atomic.h"
// Shared mutex.
class shared_mutex final
{
enum : s64
{
c_one = 1ull << 31, // Fixed-point 1.0 value (one writer)
c_min = 0x00000001, // Fixed-point 1.0/max_readers value
c_sig = 1ull << 62,
c_max = c_one
};
atomic_t<s64> m_value{c_one}; // Semaphore-alike counter
void imp_lock_shared(s64 _old);
void imp_unlock_shared(s64 _old);
void imp_wait(s64 _old);
void imp_lock(s64 _old);
void imp_unlock(s64 _old);
void imp_lock_upgrade();
void imp_lock_degrade();
public:
constexpr shared_mutex() = default;
bool try_lock_shared();
void lock_shared()
{
const s64 value = m_value.load();
// Fast path: decrement if positive
if (UNLIKELY(value < c_min || value > c_one || !m_value.compare_and_swap_test(value, value - c_min)))
{
imp_lock_shared(value);
}
}
void unlock_shared()
{
// Unconditional increment
const s64 value = m_value.fetch_add(c_min);
if (value < 0 || value > c_one - c_min)
{
imp_unlock_shared(value);
}
}
bool try_lock();
void lock()
{
// Try to lock
const s64 value = m_value.compare_and_swap(c_one, 0);
if (value != c_one)
{
imp_lock(value);
}
}
void unlock()
{
// Unconditional increment
const s64 value = m_value.fetch_add(c_one);
if (value != 0)
{
imp_unlock(value);
}
}
bool try_lock_upgrade();
void lock_upgrade()
{
if (!m_value.compare_and_swap_test(c_one - c_min, 0))
{
imp_lock_upgrade();
}
}
bool try_lock_degrade();
void lock_degrade()
{
if (!m_value.compare_and_swap_test(0, c_one - c_min))
{
imp_lock_degrade();
}
}
};
// Simplified shared (reader) lock implementation.
class reader_lock final
{
shared_mutex& m_mutex;
bool m_upgraded = false;
void lock()
{
m_upgraded ? m_mutex.lock() : m_mutex.lock_shared();
}
void unlock()
{
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
}
friend class cond_variable;
public:
reader_lock(const reader_lock&) = delete;
explicit reader_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
lock();
}
// One-way lock upgrade
void upgrade()
{
if (!m_upgraded)
{
m_mutex.lock_upgrade();
m_upgraded = true;
}
}
~reader_lock()
{
unlock();
}
};
// Simplified exclusive (writer) lock implementation.
class writer_lock final
{
shared_mutex& m_mutex;
void lock()
{
m_mutex.lock();
}
void unlock()
{
m_mutex.unlock();
}
friend class cond_variable;
public:
writer_lock(const writer_lock&) = delete;
explicit writer_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
lock();
}
~writer_lock()
{
unlock();
}
};
+2
View File
@@ -1,5 +1,7 @@
#include "stdafx.h"
#include "restore_new.h"
#include "Utilities/rXml.h"
#include "define_new_memleakdetect.h"
rXmlNode::rXmlNode() : handle()
{
+103
View File
@@ -0,0 +1,103 @@
#include "sema.h"
#include "sync.h"
void semaphore_base::imp_wait()
{
for (int i = 0; i < 10; i++)
{
busy_wait();
const s32 value = m_value.load();
if (value > 0 && m_value.compare_and_swap_test(value, value - 1))
{
return;
}
}
#ifdef _WIN32
const s32 value = m_value.fetch_sub(1);
if (value <= 0)
{
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
}
#else
while (true)
{
// Try hard way
const s32 value = m_value.op_fetch([](s32& value)
{
// Use sign bit to acknowledge waiter presence
if (value && value > INT32_MIN)
{
value--;
if (value < 0)
{
// Remove sign bit
value -= INT32_MIN;
}
}
else
{
// Set sign bit
value = INT32_MIN;
}
});
if (value >= 0)
{
// Signal other waiter to wake up or to restore sign bit
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
return;
}
futex(&m_value.raw(), FUTEX_WAIT_PRIVATE, value, nullptr, nullptr, 0);
}
#endif
}
void semaphore_base::imp_post(s32 _old)
{
verify("semaphore_base: overflow" HERE), _old < 0;
#ifdef _WIN32
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
#else
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
#endif
}
bool semaphore_base::try_wait()
{
// Conditional decrement
const s32 value = m_value.fetch_op([](s32& value)
{
if (value > 0)
{
value -= 1;
}
});
return value > 0;
}
bool semaphore_base::try_post(s32 _max)
{
// Conditional increment
const s32 value = m_value.fetch_op([&](s32& value)
{
if (value < _max)
{
value += 1;
}
});
if (value < 0)
{
imp_post(value);
}
return value < _max;
}
+146
View File
@@ -0,0 +1,146 @@
#pragma once
#include "types.h"
#include "Atomic.h"
// Lightweight semaphore helper class
class semaphore_base
{
// Semaphore value
atomic_t<s32> m_value;
void imp_wait();
void imp_post(s32 _old);
friend class semaphore_lock;
protected:
explicit constexpr semaphore_base(s32 value)
: m_value{value}
{
}
void wait()
{
// Load value
const s32 value = m_value.load();
// Conditional decrement
if (UNLIKELY(value <= 0 || !m_value.compare_and_swap_test(value, value - 1)))
{
imp_wait();
}
}
bool try_wait();
void post(s32 _max)
{
// Unconditional increment
const s32 value = m_value.fetch_add(1);
if (UNLIKELY(value < 0 || value >= _max))
{
imp_post(value);
}
}
bool try_post(s32 _max);
public:
// Get current semaphore value
s32 get() const
{
// Load value
const s32 value = m_value;
// Return only positive value
return value < 0 ? 0 : value;
}
};
// Lightweight semaphore template (default arguments define binary semaphore and Def == Max)
template <s32 Max = 1, s32 Def = Max>
class semaphore final : public semaphore_base
{
static_assert(Max >= 0, "semaphore<>: Max is out of bounds");
static_assert(Def >= 0, "semaphore<>: Def is out of bounds");
static_assert(Def <= Max, "semaphore<>: Def is too big");
using base = semaphore_base;
public:
// Default constructor (recommended)
constexpr semaphore()
: base{Def}
{
}
// Explicit value constructor (not recommended)
explicit constexpr semaphore(s32 value)
: base{value}
{
}
// Obtain a semaphore
void wait()
{
return base::wait();
}
// Try to obtain a semaphore
explicit_bool_t try_wait()
{
return base::try_wait();
}
// Return a semaphore
void post()
{
return base::post(Max);
}
// Try to return a semaphore
explicit_bool_t try_post()
{
return base::try_post(Max);
}
// Get max semaphore value
static constexpr s32 size()
{
return Max;
}
};
class semaphore_lock
{
semaphore_base& m_base;
void lock()
{
m_base.wait();
}
void unlock()
{
m_base.post(INT32_MAX);
}
friend class cond_variable;
public:
explicit semaphore_lock(const semaphore_lock&) = delete;
semaphore_lock(semaphore_base& sema)
: m_base(sema)
{
lock();
}
~semaphore_lock()
{
unlock();
}
};
+135 -175
View File
@@ -4,185 +4,145 @@
#include "types.h"
#include "Atomic.h"
#include "dynamic_library.h"
#ifdef _WIN32
#include <Windows.h>
#define DYNAMIC_IMPORT(handle, name) do { name = reinterpret_cast<decltype(name)>(GetProcAddress(handle, #name)); } while (0)
static NTSTATUS(*NtSetTimerResolution)(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
static NTSTATUS(*NtWaitForKeyedEvent)(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
static NTSTATUS(*NtReleaseKeyedEvent)(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
namespace util
{
static const bool keyed_init = []
{
const auto handle = LoadLibraryA("ntdll.dll");
DYNAMIC_IMPORT(handle, NtSetTimerResolution);
DYNAMIC_IMPORT(handle, NtWaitForKeyedEvent);
DYNAMIC_IMPORT(handle, NtReleaseKeyedEvent);
FreeLibrary(handle);
ULONG res = 100;
NtSetTimerResolution(100, TRUE, &res);
return NtWaitForKeyedEvent && NtReleaseKeyedEvent;
}();
// Wait for specified condition. func() acknowledges success by value modification.
template<typename F>
inline void keyed_wait(atomic_t<u32>& key, F&& func)
{
while (true)
{
u32 read = key.load();
u32 copy = read;
while (func(read), read != copy)
{
read = key.compare_and_swap(copy, read);
if (copy == read)
{
return;
}
copy = read;
}
NtWaitForKeyedEvent(NULL, &key, FALSE, NULL);
}
}
// Try to wake up a thread.
inline bool keyed_post(atomic_t<u32>& key, u32 acknowledged_value)
{
LARGE_INTEGER timeout;
timeout.QuadPart = -50;
while (UNLIKELY(NtReleaseKeyedEvent(NULL, &key, FALSE, &timeout) != ERROR_SUCCESS))
{
if (key.load() != acknowledged_value)
return false;
}
return true;
}
struct native_rwlock
{
SRWLOCK rwlock = SRWLOCK_INIT;
constexpr native_rwlock() = default;
native_rwlock(const native_rwlock&) = delete;
void lock()
{
AcquireSRWLockExclusive(&rwlock);
}
bool try_lock()
{
return TryAcquireSRWLockExclusive(&rwlock) != 0;
}
void unlock()
{
ReleaseSRWLockExclusive(&rwlock);
}
void lock_shared()
{
AcquireSRWLockShared(&rwlock);
}
bool try_lock_shared()
{
return TryAcquireSRWLockShared(&rwlock) != 0;
}
void unlock_shared()
{
ReleaseSRWLockShared(&rwlock);
}
};
struct native_cond
{
CONDITION_VARIABLE cond = CONDITION_VARIABLE_INIT;
constexpr native_cond() = default;
native_cond(const native_cond&) = delete;
void notify_one()
{
WakeConditionVariable(&cond);
}
void notify_all()
{
WakeAllConditionVariable(&cond);
}
void wait(native_rwlock& rwlock)
{
SleepConditionVariableSRW(&cond, &rwlock.rwlock, INFINITE, 0);
}
void wait_shared(native_rwlock& rwlock)
{
SleepConditionVariableSRW(&cond, &rwlock.rwlock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);
}
};
class exclusive_lock
{
native_rwlock& m_rwlock;
public:
exclusive_lock(native_rwlock& rwlock)
: m_rwlock(rwlock)
{
m_rwlock.lock();
}
~exclusive_lock()
{
m_rwlock.unlock();
}
};
class shared_lock
{
native_rwlock& m_rwlock;
public:
shared_lock(native_rwlock& rwlock)
: m_rwlock(rwlock)
{
m_rwlock.lock_shared();
}
~shared_lock()
{
m_rwlock.unlock_shared();
}
};
}
#include <time.h>
#elif __linux__
#include <errno.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/time.h>
#include <unistd.h>
#else
#endif
#include <ctime>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <unordered_map>
namespace util
{
struct native_rwlock;
struct native_cond;
}
#ifdef _WIN32
DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution));
DYNAMIC_IMPORT("ntdll.dll", NtWaitForKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
DYNAMIC_IMPORT("ntdll.dll", NtReleaseKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
DYNAMIC_IMPORT("ntdll.dll", NtDelayExecution, NTSTATUS(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval));
#endif
CHECK_SIZE_ALIGN(util::native_rwlock, sizeof(void*), alignof(void*));
CHECK_SIZE_ALIGN(util::native_cond, sizeof(void*), alignof(void*));
#ifndef __linux__
enum
{
FUTEX_PRIVATE_FLAG = 0,
FUTEX_WAIT = 0,
FUTEX_WAIT_PRIVATE = FUTEX_WAIT,
FUTEX_WAKE = 1,
FUTEX_WAKE_PRIVATE = FUTEX_WAKE,
FUTEX_BITSET = 2,
FUTEX_WAIT_BITSET = FUTEX_WAIT | FUTEX_BITSET,
FUTEX_WAIT_BITSET_PRIVATE = FUTEX_WAIT_BITSET,
FUTEX_WAKE_BITSET = FUTEX_WAKE | FUTEX_BITSET,
FUTEX_WAKE_BITSET_PRIVATE = FUTEX_WAKE_BITSET,
};
#endif
inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int* uaddr2, int val3)
{
#ifdef __linux__
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr, val3);
#else
static struct futex_map
{
struct waiter
{
int val;
uint mask;
std::condition_variable cv;
};
std::mutex mutex;
std::unordered_multimap<int*, waiter*, pointer_hash<int>> map;
int operator()(int* uaddr, int futex_op, int val, const timespec* timeout, int*, uint val3)
{
std::unique_lock<std::mutex> lock(mutex);
switch (futex_op)
{
case FUTEX_WAIT:
{
val3 = -1;
// Fallthrough
}
case FUTEX_WAIT_BITSET:
{
if (*(volatile int*)uaddr != val)
{
errno = EAGAIN;
return -1;
}
waiter rec;
rec.val = val;
rec.mask = val3;
const auto& ref = *map.emplace(uaddr, &rec);
int res = 0;
if (!timeout)
{
rec.cv.wait(lock, [&] { return !rec.mask; });
}
else if (futex_op == FUTEX_WAIT)
{
const auto nsec = std::chrono::nanoseconds(timeout->tv_nsec + timeout->tv_sec * 1000000000ull);
if (!rec.cv.wait_for(lock, nsec, [&] { return !rec.mask; }))
{
res = -1;
errno = ETIMEDOUT;
}
}
else
{
// TODO
}
map.erase(std::find(map.find(uaddr), map.end(), ref));
return res;
}
case FUTEX_WAKE:
{
val3 = -1;
// Fallthrough
}
case FUTEX_WAKE_BITSET:
{
int res = 0;
for (auto range = map.equal_range(uaddr); val && range.first != range.second; range.first++)
{
auto& entry = *range.first->second;
if (entry.mask & val3)
{
entry.cv.notify_one();
entry.mask = 0;
res++;
val--;
}
}
return res;
}
}
errno = EINVAL;
return -1;
}
} g_futex;
return g_futex(uaddr, futex_op, val, timeout, uaddr2, val3);
#endif
}
+55 -40
View File
@@ -11,6 +11,7 @@
#include <cstdint>
#include <type_traits>
#include <utility>
#include <chrono>
// Assume little-endian
#define IS_LE_MACHINE 1
@@ -30,8 +31,6 @@
#define SAFE_BUFFERS
#define NEVER_INLINE __attribute__((noinline))
#define FORCE_INLINE __attribute__((always_inline)) inline
// Some platforms don't support thread_local well yet.
#define thread_local __thread
#endif
@@ -39,7 +38,6 @@
#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
#define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
#define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
#define CHECK_STORAGE(type, storage) static_assert(sizeof(type) <= sizeof(storage) && alignof(type) <= alignof(decltype(storage)), #type " is too small")
// Return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
#define SIZE_32(...) static_cast<u32>(sizeof(__VA_ARGS__))
@@ -84,6 +82,10 @@ using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
using steady_clock = std::conditional<
std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock, std::chrono::steady_clock>::type;
namespace gsl
{
enum class byte : u8;
@@ -533,7 +535,7 @@ struct verify_impl
// Verification (can be safely disabled)
if (!verify_func()(std::forward<T>(value)))
{
fmt::raw_verify_error(cause, fmt::get_type_info<uint>(), N);
fmt::raw_verify_error(cause, nullptr, N);
}
return verify_impl<N + 1>{cause};
@@ -843,34 +845,6 @@ struct multicast<T, Value, void>
}
};
// Tagged ID type
template <typename T = void, typename ID = u32>
class id_value
{
// Initial value
mutable ID m_value{static_cast<ID>(-1)};
// Allow access for ID manager
friend class idm;
// Update ID
void operator=(const ID& value) const
{
m_value = value;
}
public:
constexpr id_value()
{
}
// Get the value
operator ID() const
{
return m_value;
}
};
// Error code type (return type), implements error reporting. Could be a template.
struct error_code
{
@@ -926,13 +900,54 @@ constexpr FORCE_INLINE error_code::not_an_error not_an_error(const T& value)
return static_cast<error_code::not_an_error>(static_cast<s32>(value));
}
template <typename T, typename ID>
struct fmt_unveil<id_value<T, ID>>
// Synchronization helper (cache-friendly busy waiting)
inline void busy_wait(std::size_t count = 100)
{
using type = typename fmt_unveil<ID>::type;
while (count--) _mm_pause();
}
static inline auto get(const id_value<T, ID>& value)
{
return fmt_unveil<ID>::get(value);
}
};
// Rotate helpers
#if defined(__GNUG__)
inline u8 rol8(const u8 x, const u8 n)
{
u8 result = x;
__asm__("rolb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u16 rol16(const u16 x, const u16 n)
{
u16 result = x;
__asm__("rolw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u32 rol32(const u32 x, const u32 n)
{
u32 result = x;
__asm__("roll %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u64 rol64(const u64 x, const u64 n)
{
u64 result = x;
__asm__("rolq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u64 ror64(const u64 x, const u64 n)
{
u64 result = x;
__asm__("rorq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
#elif defined(_MSC_VER)
inline u8 rol8(const u8 x, const u8 n) { return _rotl8(x, n); }
inline u16 rol16(const u16 x, const u16 n) { return _rotl16(x, (u8)n); }
inline u32 rol32(const u32 x, const u32 n) { return _rotl(x, (int)n); }
inline u64 rol64(const u64 x, const u64 n) { return _rotl64(x, (int)n); }
inline u64 ror64(const u64 x, const u64 n) { return _rotr64(x, (int)n); }
#endif
-10
View File
@@ -73,16 +73,6 @@ namespace std {
}; // class bad_variant_access
template <typename R = void>
struct MAPBOX_VARIANT_DEPRECATED static_visitor
{
using result_type = R;
protected:
static_visitor() {}
~static_visitor() {}
};
namespace detail {
static constexpr std::size_t invalid_value = std::size_t(-1);
+11 -7
View File
@@ -1,19 +1,22 @@
version: 1.0.{build}
version: '0.0.2-{build}'
os: Visual Studio 2015
configuration:
# - Release
- ReleaseLLVM
platform: x64
clone_folder: c:\rpcs3
clone_folder: C:\rpcs3
clone_depth: 3
test: off
configuration:
# Release
- ReleaseLLVM
before_build:
- ps: $env:Date="$(git show -s --date=short --format='%ad')"
- git submodule update --init 3rdparty/ffmpeg 3rdparty/pugixml asmjit 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp rsx_program_decompiler 3rdparty/cereal
- 7z x wxWidgets.7z -aos -oC:\rpcs3\wxWidgets > null
- 7z x zlib.7z -aos -oC:\rpcs3\ > null
- 7z x vulkan.7z -aos -oC:\rpcs3\Vulkan > null
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/)
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
else (7z x llvmlibs.7z -aos -oC:\rpcs3 > null && cmake -G "Visual Studio 14 Win64" -DLLVM_DIR=C:/rpcs3/llvm_build/share/llvm/cmake -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
build_script:
@@ -31,4 +34,5 @@ install:
artifacts:
- path: bin
name: rpcs3-$(configuration)-$(COMMIT_SHA)
name: 'rpcs3-v0.0.2-$(Date)-$(COMMIT_SHA)_win64'
type: zip
-2
View File
@@ -2,5 +2,3 @@
*
# Except this file
!.gitignore
!cache
!cache/README.txt
-1
View File
@@ -1 +0,0 @@
This directory is used for cache functionality, so don't store any data here.
-1
View File
@@ -1 +0,0 @@
This directory is used for temporary gamedata functionality, so don't store any data here.
+1 -1
View File
@@ -1,4 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
!.gitignore
+11 -3
View File
@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 2.8.12)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake_modules")
set(RES_FILES "")
include(cotire)
project(rpcs3)
@@ -15,9 +16,16 @@ add_custom_command(OUTPUT something_that_never_exists
# Check for a sufficient compiler and set build options
include(ConfigureCompiler)
if(WIN32)
add_definitions(-DUNICODE)
add_definitions(-D_WIN32_WINNT=0x0601)
set(RES_FILES "rpcs3_.rc")
if(NOT MSVC)
set(CMAKE_RC_COMPILER_INIT windres)
enable_language(RC)
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
endif()
endif()
if(NOT MSVC)
@@ -193,7 +201,7 @@ if(NOT WIN32)
endforeach(TMP_PATH)
endif()
add_executable(rpcs3 ${RPCS3_SRC})
add_executable(rpcs3 ${RPCS3_SRC} ${RES_FILES})
if(MSVC)
@@ -201,7 +209,7 @@ if(MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /DYNAMICBASE:NO /BASE:0x10000 /FIXED")
endif()
if(WIN32)
target_link_libraries(rpcs3 ws2_32.lib Winmm.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV HLSL)
target_link_libraries(rpcs3 ws2_32.lib Winmm.lib Psapi.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV HLSL)
if(NOT MSVC)
target_link_libraries(rpcs3 ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} opengl32.lib glu32.lib libpthread)
else()
@@ -210,7 +218,7 @@ if(WIN32)
target_link_libraries(rpcs3 avformat.lib avcodec.lib avutil.lib swresample.lib swscale.lib png16_static ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${ADDITIONAL_LIBS})
else()
target_link_libraries(rpcs3 ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${GLEW_LIBRARY} ${OPENGL_LIBRARIES})
target_link_libraries(rpcs3 ${ZLIB_LIBRARIES} ${ADDITIONAL_LIBS})
target_link_libraries(rpcs3 -ldl ${ZLIB_LIBRARIES} ${ADDITIONAL_LIBS})
if (USE_SYSTEM_FFMPEG)
link_libraries(${FFMPEG_LIBRARY_DIR})
target_link_libraries(rpcs3 libavformat.so libavcodec.so libavutil.so libswresample.so libswscale.so)
+1 -1
View File
@@ -1,5 +1,5 @@
#pragma once
#define POLARSSL_AES_ROM_TABLES
/**
* \file aes.h
*
+9
View File
@@ -140,6 +140,15 @@ static u8 VSH_PUB[0x28] = {
0x6E, 0x73, 0x6A, 0xBF, 0x81, 0xF7, 0x0E, 0xE9, 0x16, 0x1B, 0x0D, 0xDE, 0xB0, 0x26, 0x76, 0x1A, 0xFF, 0x7B, 0xC8, 0x5B
};
static u8 SCEPKG_RIV[0x10] = {
0x4A, 0xCE, 0xF0, 0x12, 0x24, 0xFB, 0xEE, 0xDF, 0x82, 0x45, 0xF8, 0xFF, 0x10, 0x21, 0x1E, 0x6E
};
static u8 SCEPKG_ERK[0x20] = {
0xA9, 0x78, 0x18, 0xBD, 0x19, 0x3A, 0x67, 0xA1, 0x6F, 0xE8, 0x3A, 0x85, 0x5E, 0x1B, 0xE9, 0xFB, 0x56, 0x40, 0x93, 0x8D,
0x4D, 0xBC, 0xB2, 0xCB, 0x52, 0xC5, 0xA2, 0xF8, 0xB0, 0x2B, 0x10, 0x31
};
class KeyVault
{
std::vector<SELF_KEY> sk_LV0_arr;
+1 -2
View File
@@ -140,8 +140,7 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size)
unsigned int range = 0xFFFFFFFF;
unsigned int code = (in[1] << 24) | (in[2] << 16) | (in[3] << 8) | in[4];
// TODO:: Syphurith: There was a check against the unsigned char head. if (head < 0) would always be false.. I don't know are you tried to if (head > 0x80)?
if (head < 0) // Check if we have a valid starting byte.
if (head > 0x80) // Check if we have a valid starting byte.
{
// The dictionary header is invalid, the data is not compressed.
result = -1;
+454 -376
View File
File diff suppressed because it is too large Load Diff
+108 -25
View File
@@ -2,35 +2,118 @@
#include <stdio.h>
#include <string.h>
#include <array>
#include "utils.h"
#define SDAT_FLAG 0x01000000
#define EDAT_COMPRESSED_FLAG 0x00000001
#define EDAT_FLAG_0x02 0x00000002
#define EDAT_ENCRYPTED_KEY_FLAG 0x00000008
#define EDAT_FLAG_0x10 0x00000010
#define EDAT_FLAG_0x20 0x00000020
#define EDAT_DEBUG_DATA_FLAG 0x80000000
constexpr u32 SDAT_FLAG = 0x01000000;
constexpr u32 EDAT_COMPRESSED_FLAG = 0x00000001;
constexpr u32 EDAT_FLAG_0x02 = 0x00000002;
constexpr u32 EDAT_ENCRYPTED_KEY_FLAG = 0x00000008;
constexpr u32 EDAT_FLAG_0x10 = 0x00000010;
constexpr u32 EDAT_FLAG_0x20 = 0x00000020;
constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000;
typedef struct
struct EdatKeys_t
{
unsigned char magic[4];
int version;
int license;
int type;
unsigned char content_id[0x30];
unsigned char digest[0x10];
unsigned char title_hash[0x10];
unsigned char dev_hash[0x10];
unsigned long long unk1;
unsigned long long unk2;
} NPD_HEADER;
std::array<u8, 0x10> devKlic{};
std::array<u8, 0x10> rifKey{};
};
typedef struct
struct NPD_HEADER
{
int flags;
int block_size;
unsigned long long file_size;
} EDAT_HEADER;
u32 magic;
s32 version;
s32 license;
s32 type;
u8 content_id[0x30];
u8 digest[0x10];
u8 title_hash[0x10];
u8 dev_hash[0x10];
u64 unk1;
u64 unk2;
};
int DecryptEDAT(const std::string& input_file_name, const std::string& output_file_name, int mode, const std::string& rap_file_name, unsigned char *custom_klic, bool verbose);
struct EDAT_HEADER
{
s32 flags;
s32 block_size;
u64 file_size;
};
// Decrypts full file, or null/empty file
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose);
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const std::array<u8,0x10>& custom_klic);
extern std::array<u8, 0x10> GetEdatRifKeyFromRapFile(const fs::file& rap_file);
struct EDATADecrypter final : fs::file_base
{
// file stream
const fs::file edata_file;
u64 file_size{0};
u32 total_blocks{0};
u64 pos{0};
NPD_HEADER npdHeader;
EDAT_HEADER edatHeader;
// Internal data buffers.
std::unique_ptr<u8[]> data_buf;
u64 data_buf_size{0};
std::array<u8, 0x10> dec_key{};
// edat usage
std::array<u8, 0x10> rif_key{};
std::array<u8, 0x10> dev_key{};
public:
// SdataByFd usage
EDATADecrypter(fs::file&& input)
: edata_file(std::move(input)) {}
// Edat usage
EDATADecrypter(fs::file&& input, const std::array<u8, 0x10>& dev_key, const std::array<u8, 0x10>& rif_key)
: edata_file(std::move(input)), rif_key(rif_key), dev_key(dev_key) {}
~EDATADecrypter() override {}
// false if invalid
bool ReadHeader();
u64 ReadData(u64 pos, u8* data, u64 size);
fs::stat_t stat() override
{
fs::stat_t stats;
stats.is_directory = false;
stats.is_writable = false;
stats.size = file_size;
stats.atime = -1;
stats.ctime = -1;
stats.mtime = -1;
return stats;
}
bool trunc(u64 length) override
{
return true;
};
u64 read(void* buffer, u64 size) override
{
u64 bytesRead = ReadData(pos, (u8*)buffer, size);
pos += bytesRead;
return bytesRead;
}
u64 write(const void* buffer, u64 size) override
{
return 0;
}
u64 seek(s64 offset, fs::seek_mode whence) override
{
return
whence == fs::seek_set ? pos = offset :
whence == fs::seek_cur ? pos = offset + pos :
whence == fs::seek_end ? pos = offset + size() :
(fmt::raw_error("EDATADecrypter::seek(): invalid whence"), 0);
}
u64 size() override { return file_size; }
};
+74 -11
View File
@@ -49,12 +49,6 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
}
}
if (header.header_size != PKG_HEADER_SIZE && header.header_size != PKG_HEADER_SIZE2)
{
LOG_ERROR(LOADER, "Wrong PKG header size (0x%x)", header.header_size);
return false;
}
if (header.pkg_size > pkg_f.size())
{
LOG_ERROR(LOADER, "PKG file size mismatch (pkg_size=0x%llx)", header.pkg_size);
@@ -67,11 +61,54 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
return false;
}
be_t<u32> drm_type{0};
be_t<u32> content_type{0};
pkg_f.seek(header.pkg_info_off);
for (u32 i = 0; i < header.pkg_info_num; i++)
{
struct packet_T
{
be_t<u32> id;
be_t<u32> size;
} packet;
pkg_f.read(packet);
// TODO
switch (+packet.id)
{
case 0x1:
{
if (packet.size == sizeof(drm_type))
{
pkg_f.read(drm_type);
continue;
}
break;
}
case 0x2:
{
if (packet.size == sizeof(content_type))
{
pkg_f.read(content_type);
continue;
}
break;
}
}
pkg_f.seek(packet.size, fs::seek_cur);
}
// Allocate buffer with BUF_SIZE size or more if required
const std::unique_ptr<u128[]> buf(new u128[std::max<u64>(BUF_SIZE, sizeof(PKGEntry) * header.file_count) / sizeof(u128)]);
// Define decryption subfunction (`psp` arg selects the key for specific block)
auto decrypt = [&](u64 offset, u64 size, bool psp) -> u64
auto decrypt = [&](u64 offset, u64 size, const uchar* key) -> u64
{
pkg_f.seek(start_offset + header.data_offset + offset);
@@ -114,7 +151,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
aes_context ctx;
// Set encryption key for stream cipher
aes_setkey_enc(&ctx, psp ? PKG_AES_KEY2 : PKG_AES_KEY, 128);
aes_setkey_enc(&ctx, key, 128);
// Initialize stream cipher for start position
be_t<u128> input = header.klicensee.value() + offset / 16;
@@ -134,7 +171,24 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
return read;
};
decrypt(0, header.file_count * sizeof(PKGEntry), header.pkg_platform == PKG_PLATFORM_TYPE_PSP);
std::array<uchar, 16> dec_key;
if (header.pkg_platform == PKG_PLATFORM_TYPE_PSP && content_type >= 0x15 && content_type <= 0x17)
{
const uchar psp2t1[] = {0xE3, 0x1A, 0x70, 0xC9, 0xCE, 0x1D, 0xD7, 0x2B, 0xF3, 0xC0, 0x62, 0x29, 0x63, 0xF2, 0xEC, 0xCB};
const uchar psp2t2[] = {0x42, 0x3A, 0xCA, 0x3A, 0x2B, 0xD5, 0x64, 0x9F, 0x96, 0x86, 0xAB, 0xAD, 0x6F, 0xD8, 0x80, 0x1F};
const uchar psp2t3[] = {0xAF, 0x07, 0xFD, 0x59, 0x65, 0x25, 0x27, 0xBA, 0xF1, 0x33, 0x89, 0x66, 0x8B, 0x17, 0xD9, 0xEA};
aes_context ctx;
aes_setkey_enc(&ctx, content_type == 0x15 ? psp2t1 : content_type == 0x16 ? psp2t2 : psp2t3, 128);
aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const uchar*>(&header.klicensee), dec_key.data());
decrypt(0, header.file_count * sizeof(PKGEntry), dec_key.data());
}
else
{
std::memcpy(dec_key.data(), PKG_AES_KEY, dec_key.size());
decrypt(0, header.file_count * sizeof(PKGEntry), header.pkg_platform == PKG_PLATFORM_TYPE_PSP ? PKG_AES_KEY2 : dec_key.data());
}
std::vector<PKGEntry> entries(header.file_count);
@@ -150,10 +204,12 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
continue;
}
decrypt(entry.name_offset, entry.name_size, is_psp);
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : dec_key.data());
const std::string name(reinterpret_cast<char*>(buf.get()), entry.name_size);
LOG_NOTICE(LOADER, "Entry 0x%08x: %s", entry.type, name);
switch (entry.type & 0xff)
{
case PKG_FILE_ENTRY_NPDRM:
@@ -161,6 +217,12 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
case PKG_FILE_ENTRY_SDAT:
case PKG_FILE_ENTRY_REGULAR:
case PKG_FILE_ENTRY_UNK1:
case 0xe:
case 0x10:
case 0x11:
case 0x13:
case 0x15:
case 0x16:
{
const std::string path = dir + name;
@@ -172,7 +234,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
{
const u64 block_size = std::min<u64>(BUF_SIZE, entry.file_size - pos);
if (decrypt(entry.file_offset + pos, block_size, is_psp) != block_size)
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : dec_key.data()) != block_size)
{
LOG_ERROR(LOADER, "Failed to extract file %s", path);
break;
@@ -209,6 +271,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
}
case PKG_FILE_ENTRY_FOLDER:
case 0x12:
{
const std::string path = dir + name;
+3 -3
View File
@@ -35,9 +35,9 @@ struct PKGHeader
nse_t<u32> pkg_magic; // Magic (0x7f504b47)
be_t<u16> pkg_type; // Release type (Retail:0x8000, Debug:0x0000)
be_t<u16> pkg_platform; // Platform type (PS3:0x0001, PSP:0x0002)
be_t<u32> header_size; // Header size (0xc0)
be_t<u32> unk1; // Some PKG version maybe?
be_t<u32> meta_size; // Size of metadata (block after header & hashes)
be_t<u32> pkg_info_off;
be_t<u32> pkg_info_num;
be_t<u32> header_size; // Header size
be_t<u32> file_count; // Number of files
be_t<u64> pkg_size; // PKG size in bytes
be_t<u64> data_offset; // Encrypted data offset
+287 -103
View File
@@ -3,6 +3,7 @@
#include "sha1.h"
#include "utils.h"
#include "unself.h"
#include "Emu/VFS.h"
// TODO: Still reliant on wxWidgets for zlib functions. Alternative solutions?
#include <zlib.h>
@@ -647,6 +648,236 @@ void SelfHeader::Load(const fs::file& f)
pad = Read64(f);
}
SCEDecrypter::SCEDecrypter(const fs::file& s)
: sce_f(s)
, data_buf_length(0)
{
}
bool SCEDecrypter::LoadHeaders()
{
// Read SCE header.
sce_f.seek(0);
sce_hdr.Load(sce_f);
// Check SCE magic.
if (!sce_hdr.CheckMagic())
{
LOG_ERROR(LOADER, "SELF: Not a SELF file!");
return false;
}
return true;
}
bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
{
aes_context aes;
u32 metadata_info_size = SIZE_32(meta_info);
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
// Locate and read the encrypted metadata info.
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
sce_f.read(metadata_info.get(), metadata_info_size);
// Locate and read the encrypted metadata header and section header.
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
sce_f.read(metadata_headers.get(), metadata_headers_size);
// Copy the necessary parameters.
u8 metadata_key[0x20];
u8 metadata_iv[0x10];
memcpy(metadata_key, erk, 0x20);
memcpy(metadata_iv, riv, 0x10);
// Check DEBUG flag.
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
{
// Decrypt the metadata info.
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
}
// Load the metadata info.
meta_info.Load(metadata_info.get());
// If the padding is not NULL for the key or iv fields, the metadata info
// is not properly decrypted.
if ((meta_info.key_pad[0] != 0x00) ||
(meta_info.iv_pad[0] != 0x00))
{
LOG_ERROR(LOADER, "SELF: Failed to decrypt metadata info!");
return false;
}
// Perform AES-CTR encryption on the metadata headers.
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
aes_setkey_enc(&aes, meta_info.key, 128);
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
// Load the metadata header.
meta_hdr.Load(metadata_headers.get());
// Load the metadata section headers.
meta_shdr.clear();
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
meta_shdr.emplace_back();
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
}
// Copy the decrypted data keys.
data_keys_length = meta_hdr.key_count * 0x10;
data_keys = std::make_unique<u8[]>(data_keys_length);
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
return true;
}
bool SCEDecrypter::DecryptData()
{
aes_context aes;
// Calculate the total data size.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
data_buf_length += meta_shdr[i].data_size;
}
// Allocate a buffer to store decrypted data.
data_buf = std::make_unique<u8[]>(data_buf_length);
// Set initial offset.
u32 data_buf_offset = 0;
// Parse the metadata section headers to find the offsets of encrypted data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
u8 data_key[0x10];
u8 data_iv[0x10];
// Check if this is an encrypted section.
if (meta_shdr[i].encrypted == 3)
{
// Make sure the key and iv are not out of boundaries.
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
{
// Get the key and iv from the previously stored key buffer.
memcpy(data_key, data_keys.get() + meta_shdr[i].key_idx * 0x10, 0x10);
memcpy(data_iv, data_keys.get() + meta_shdr[i].iv_idx * 0x10, 0x10);
// Allocate a buffer to hold the data.
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
// Seek to the section data offset and read the encrypted data.
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
// Zero out our ctr nonce.
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
// Perform AES-CTR encryption on the data blocks.
aes_setkey_enc(&aes, data_key, 128);
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
// Copy the decrypted data.
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
}
}
else
{
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
}
// Advance the buffer's offset.
data_buf_offset += meta_shdr[i].data_size;
}
return true;
}
// Each section gets put into its own file.
std::vector<fs::file> SCEDecrypter::MakeFile()
{
std::vector<fs::file> vec;
// Set initial offset.
u32 data_buf_offset = 0;
// Write data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
fs::file out_f = fs::make_stream<std::vector<u8>>();
bool isValid = true;
// Decompress if necessary.
if (meta_shdr[i].compressed == 2)
{
const size_t BUFSIZE = 32 * 1024;
u8 tempbuf[BUFSIZE];
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = meta_shdr[i].data_size;
strm.avail_out = BUFSIZE;
strm.next_in = data_buf.get()+data_buf_offset;
strm.next_out = tempbuf;
int ret = inflateInit(&strm);
while (strm.avail_in)
{
ret = inflate(&strm, Z_NO_FLUSH);
if (ret == Z_STREAM_END)
break;
if (ret != Z_OK)
isValid = false;
if (!strm.avail_out) {
out_f.write(tempbuf, BUFSIZE);
strm.next_out = tempbuf;
strm.avail_out = BUFSIZE;
}
else
break;
}
int inflate_res = Z_OK;
inflate_res = inflate(&strm, Z_FINISH);
if (inflate_res != Z_STREAM_END)
isValid = false;
out_f.write(tempbuf, BUFSIZE - strm.avail_out);
inflateEnd(&strm);
}
else
{
// Write the data.
out_f.write(data_buf.get()+data_buf_offset, meta_shdr[i].data_size);
}
// Advance the data buffer offset by data size.
data_buf_offset += meta_shdr[i].data_size;
if (out_f.pos() != out_f.size())
fmt::throw_exception("MakeELF written bytes (%llu) does not equal buffer size (%llu).", out_f.pos(), out_f.size());
if (isValid) vec.push_back(std::move(out_f));
}
return vec;
}
SELFDecrypter::SELFDecrypter(const fs::file& s)
: self_f(s)
, key_v()
@@ -904,17 +1135,17 @@ bool SELFDecrypter::LoadMetadata()
{
aes_context aes;
u32 metadata_info_size = SIZE_32(meta_info);
u8 *metadata_info = (u8 *)malloc(metadata_info_size);
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
u8 *metadata_headers = (u8 *)malloc(metadata_headers_size);
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
// Locate and read the encrypted metadata info.
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
self_f.read(metadata_info, metadata_info_size);
self_f.read(metadata_info.get(), metadata_info_size);
// Locate and read the encrypted metadata header and section header.
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
self_f.read(metadata_headers, metadata_headers_size);
self_f.read(metadata_headers.get(), metadata_headers_size);
// Find the right keyset from the key vault.
SELF_KEY keyset = key_v.FindSelfKey(app_info.self_type, sce_hdr.se_flags, app_info.version);
@@ -929,16 +1160,16 @@ bool SELFDecrypter::LoadMetadata()
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
{
// Decrypt the NPDRM layer.
if (!DecryptNPDRM(metadata_info, metadata_info_size))
if (!DecryptNPDRM(metadata_info.get(), metadata_info_size))
return false;
// Decrypt the metadata info.
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info, metadata_info);
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
}
// Load the metadata info.
meta_info.Load(metadata_info);
meta_info.Load(metadata_info.get());
// If the padding is not NULL for the key or iv fields, the metadata info
// is not properly decrypted.
@@ -953,23 +1184,23 @@ bool SELFDecrypter::LoadMetadata()
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
aes_setkey_enc(&aes, meta_info.key, 128);
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers, metadata_headers);
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
// Load the metadata header.
meta_hdr.Load(metadata_headers);
meta_hdr.Load(metadata_headers.get());
// Load the metadata section headers.
meta_shdr.clear();
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
meta_shdr.emplace_back();
meta_shdr.back().Load(metadata_headers + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
}
// Copy the decrypted data keys.
data_keys_length = meta_hdr.key_count * 0x10;
data_keys = (u8 *) malloc (data_keys_length);
memcpy(data_keys, metadata_headers + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
data_keys = std::make_unique<u8[]>(data_keys_length);
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
return true;
}
@@ -989,7 +1220,7 @@ bool SELFDecrypter::DecryptData()
}
// Allocate a buffer to store decrypted data.
data_buf = (u8*)malloc(data_buf_length);
data_buf = std::make_unique<u8[]>(data_buf_length);
// Set initial offset.
u32 data_buf_offset = 0;
@@ -1009,31 +1240,28 @@ bool SELFDecrypter::DecryptData()
if((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
{
// Get the key and iv from the previously stored key buffer.
memcpy(data_key, data_keys + meta_shdr[i].key_idx * 0x10, 0x10);
memcpy(data_iv, data_keys + meta_shdr[i].iv_idx * 0x10, 0x10);
memcpy(data_key, data_keys.get() + meta_shdr[i].key_idx * 0x10, 0x10);
memcpy(data_iv, data_keys.get() + meta_shdr[i].iv_idx * 0x10, 0x10);
// Allocate a buffer to hold the data.
u8 *buf = (u8 *)malloc(meta_shdr[i].data_size);
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
// Seek to the section data offset and read the encrypted data.
self_f.seek(meta_shdr[i].data_offset);
self_f.read(buf, meta_shdr[i].data_size);
self_f.read(buf.get(), meta_shdr[i].data_size);
// Zero out our ctr nonce.
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
// Perform AES-CTR encryption on the data blocks.
aes_setkey_enc(&aes, data_key, 128);
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf, buf);
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
// Copy the decrypted data.
memcpy(data_buf + data_buf_offset, buf, meta_shdr[i].data_size);
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
// Advance the buffer's offset.
data_buf_offset += meta_shdr[i].data_size;
// Release the temporary buffer.
free(buf);
}
}
}
@@ -1041,15 +1269,10 @@ bool SELFDecrypter::DecryptData()
return true;
}
bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
fs::file SELFDecrypter::MakeElf(bool isElf32)
{
// Create a new ELF file.
fs::file e(elf, fs::rewrite);
if(!e)
{
LOG_ERROR(LOADER, "Could not create ELF file! (%s)", elf.c_str());
return false;
}
fs::file e = fs::make_stream<std::vector<u8>>();
// Set initial offset.
u32 data_buf_offset = 0;
@@ -1072,7 +1295,7 @@ bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
{
// Seek to the program header data offset and write the data.
e.seek(phdr32_arr[meta_shdr[i].program_idx].p_offset);
e.write(data_buf + data_buf_offset, meta_shdr[i].data_size);
e.write(data_buf.get() + data_buf_offset, meta_shdr[i].data_size);
// Advance the data buffer offset by data size.
data_buf_offset += meta_shdr[i].data_size;
@@ -1122,7 +1345,7 @@ bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
// Create a buffer separate from data_buf to uncompress.
std::unique_ptr<u8[]> zlib_buf(new u8[data_buf_length]);
memcpy(zlib_buf.get(), data_buf, data_buf_length);
memcpy(zlib_buf.get(), data_buf.get(), data_buf_length);
// Use zlib uncompress on the new buffer.
// decomp_buf_length changes inside the call to uncompress, so it must be a pointer to correct type (in writeable mem space).
@@ -1145,7 +1368,7 @@ bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
{
// Seek to the program header data offset and write the data.
e.seek(phdr64_arr[meta_shdr[i].program_idx].p_offset);
e.write(data_buf + data_buf_offset, meta_shdr[i].data_size);
e.write(data_buf.get() + data_buf_offset, meta_shdr[i].data_size);
}
// Advance the data buffer offset by data size.
@@ -1165,33 +1388,25 @@ bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
}
}
return true;
return e;
}
bool SELFDecrypter::GetKeyFromRap(u8 *content_id, u8 *npdrm_key)
bool SELFDecrypter::GetKeyFromRap(u8* content_id, u8* npdrm_key)
{
// Set empty RAP key.
u8 rap_key[0x10];
memset(rap_key, 0, 0x10);
// Try to find a matching RAP file under exdata folder.
std::string ci_str((const char *)content_id);
std::string pf_str("00000001"); // TODO: Allow multiple profiles. Use default for now.
std::string rap_path("dev_hdd0/home/" + pf_str + "/exdata/" + ci_str + ".rap");
// Check if we have a valid RAP file.
if (!fs::is_file(rap_path))
{
LOG_ERROR(LOADER, "This application requires a valid RAP file for decryption!");
return false;
}
const std::string ci_str = reinterpret_cast<const char*>(content_id);
const std::string rap_path = "/dev_hdd0/home/00000001/exdata/" + ci_str + ".rap";
// Open the RAP file and read the key.
fs::file rap_file(rap_path);
const fs::file rap_file(vfs::get(rap_path));
if (!rap_file)
{
LOG_ERROR(LOADER, "Failed to load RAP file!");
LOG_FATAL(LOADER, "Failed to load RAP file: %s", rap_path);
return false;
}
@@ -1204,23 +1419,11 @@ bool SELFDecrypter::GetKeyFromRap(u8 *content_id, u8 *npdrm_key)
return true;
}
bool IsSelf(const std::string& path)
static bool IsSelfElf32(const fs::file& f)
{
fs::file f(path);
if (!f) return false;
SceHeader hdr;
hdr.Load(f);
return hdr.CheckMagic();
}
bool IsSelfElf32(const std::string& path)
{
fs::file f(path);
if (!f) return false;
f.seek(0);
SceHeader hdr;
SelfHeader sh;
@@ -1236,46 +1439,31 @@ bool IsSelfElf32(const std::string& path)
return (elf_class[4] == 1);
}
bool CheckDebugSelf(const std::string& self, const std::string& elf)
static bool CheckDebugSelf(fs::file& s)
{
// Open the SELF file.
fs::file s(self);
if (!s)
if (s.size() < 0x18)
{
LOG_ERROR(LOADER, "Could not open SELF file! (%s)", self.c_str());
return false;
}
// Get the key version.
s.seek(0x08);
u16 key_version;
s.read(&key_version, sizeof(key_version));
const u16 key_version = s.read<le_t<u16>>();
// Check for DEBUG version.
if (swap16(key_version) == 0x8000)
if (key_version == 0x80 || key_version == 0xc0)
{
LOG_WARNING(LOADER, "Debug SELF detected! Removing fake header...");
// Get the real elf offset.
s.seek(0x10);
u64 elf_offset;
s.read(&elf_offset, sizeof(elf_offset));
// Start at the real elf offset.
elf_offset = swap64(elf_offset);
s.seek(elf_offset);
s.seek(key_version == 0x80 ? +s.read<be_t<u64>>() : +s.read<le_t<u64>>());
// Write the real ELF file back.
fs::file e(elf, fs::rewrite);
if (!e)
{
LOG_ERROR(LOADER, "Could not create ELF file! (%s)", elf.c_str());
return false;
}
fs::file e = fs::make_stream<std::vector<u8>>();
// Copy the data.
char buf[2048];
@@ -1284,6 +1472,7 @@ bool CheckDebugSelf(const std::string& self, const std::string& elf)
e.write(buf, size);
}
s = std::move(e);
return true;
}
@@ -1291,53 +1480,48 @@ bool CheckDebugSelf(const std::string& self, const std::string& elf)
return false;
}
bool DecryptSelf(const std::string& elf, const std::string& self)
{
LOG_NOTICE(LOADER, "Decrypting %s", self);
// Check for a debug SELF first.
if (!CheckDebugSelf(self, elf))
extern fs::file decrypt_self(fs::file elf_or_self)
{
if (!elf_or_self)
{
// Set a virtual pointer to the SELF file.
fs::file self_vf(self);
if (!self_vf)
return false;
return fs::file{};
}
elf_or_self.seek(0);
// Check SELF header first. Check for a debug SELF.
if (elf_or_self.size() >= 4 && elf_or_self.read<u32>() == "SCE\0"_u32 && !CheckDebugSelf(elf_or_self))
{
// Check the ELF file class (32 or 64 bit).
bool isElf32 = IsSelfElf32(self);
bool isElf32 = IsSelfElf32(elf_or_self);
// Start the decrypter on this SELF file.
SELFDecrypter self_dec(self_vf);
SELFDecrypter self_dec(elf_or_self);
// Load the SELF file headers.
if (!self_dec.LoadHeaders(isElf32))
{
LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
return false;
return fs::file{};
}
// Load and decrypt the SELF file metadata.
if (!self_dec.LoadMetadata())
{
LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!");
return false;
return fs::file{};
}
// Decrypt the SELF file data.
if (!self_dec.DecryptData())
{
LOG_ERROR(LOADER, "SELF: Failed to decrypt SELF file data!");
return false;
return fs::file{};
}
// Make a new ELF file from this SELF.
if (!self_dec.MakeElf(elf, isElf32))
{
LOG_ERROR(LOADER, "SELF: Failed to make ELF file from SELF!");
return false;
}
return self_dec.MakeElf(isElf32);
}
return true;
return elf_or_self;
}
+32 -7
View File
@@ -338,6 +338,34 @@ struct SelfHeader
void Show(){}
};
class SCEDecrypter
{
protected:
// Main SELF file stream.
const fs::file& sce_f;
// SCE headers.
SceHeader sce_hdr;
// Metadata structs.
MetadataInfo meta_info;
MetadataHeader meta_hdr;
std::vector<MetadataSectionHeader> meta_shdr;
// Internal data buffers.
std::unique_ptr<u8[]> data_keys;
u32 data_keys_length;
std::unique_ptr<u8[]> data_buf;
u32 data_buf_length;
public:
SCEDecrypter(const fs::file& s);
std::vector<fs::file> MakeFile();
bool LoadHeaders();
bool LoadMetadata(const u8 erk[32], const u8 riv[16]);
bool DecryptData();
};
class SELFDecrypter
{
// Main SELF file stream.
@@ -369,9 +397,9 @@ class SELFDecrypter
std::vector<MetadataSectionHeader> meta_shdr;
// Internal data buffers.
u8 *data_keys;
std::unique_ptr<u8[]> data_keys;
u32 data_keys_length;
u8 *data_buf;
std::unique_ptr<u8[]> data_buf;
u32 data_buf_length;
// Main key vault instance.
@@ -379,7 +407,7 @@ class SELFDecrypter
public:
SELFDecrypter(const fs::file& s);
bool MakeElf(const std::string& elf, bool isElf32);
fs::file MakeElf(bool isElf32);
bool LoadHeaders(bool isElf32);
void ShowHeaders(bool isElf32);
bool LoadMetadata();
@@ -388,7 +416,4 @@ public:
bool GetKeyFromRap(u8 *content_id, u8 *npdrm_key);
};
extern bool IsSelf(const std::string& path);
extern bool IsSelfElf32(const std::string& path);
extern bool CheckDebugSelf(const std::string& self, const std::string& elf);
extern bool DecryptSelf(const std::string& elf, const std::string& self);
extern fs::file decrypt_self(fs::file elf_or_self);
+21 -66
View File
@@ -6,29 +6,13 @@
#include <stdio.h>
#include <time.h>
#include <memory>
// Auxiliary functions (endian swap, xor and prng).
u16 swap16(u16 i)
{
return ((i & 0xFF00) >> 8) | ((i & 0xFF) << 8);
}
u32 swap32(u32 i)
void xor_key(unsigned char *dest, const u8* src1, const u8* src2)
{
return ((i & 0xFF000000) >> 24) | ((i & 0xFF0000) >> 8) | ((i & 0xFF00) << 8) | ((i & 0xFF) << 24);
}
u64 swap64(u64 i)
{
return ((i & 0x00000000000000ff) << 56) | ((i & 0x000000000000ff00) << 40) |
((i & 0x0000000000ff0000) << 24) | ((i & 0x00000000ff000000) << 8) |
((i & 0x000000ff00000000) >> 8) | ((i & 0x0000ff0000000000) >> 24) |
((i & 0x00ff000000000000) >> 40) | ((i & 0xff00000000000000) >> 56);
}
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size)
{
int i;
for(i = 0; i < size; i++)
for(int i = 0; i < 0x10; i++)
{
dest[i] = src1[i] ^ src2[i];
}
@@ -36,16 +20,10 @@ void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int
void prng(unsigned char *dest, int size)
{
unsigned char *buffer = new unsigned char[size];
srand((u32)time(0));
int i;
for(i = 0; i < size; i++)
buffer[i] = (unsigned char)(rand() & 0xFF);
memcpy(dest, buffer, size);
delete[] buffer;
for(int i = 0; i < size; i++)
dest[i] = (unsigned char)(rand() & 0xFF);
}
// Hex string conversion auxiliary functions.
@@ -82,8 +60,8 @@ void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_len
// Don't convert if the string length is odd.
if (!(strn_length % 2))
{
u8 *out = (u8 *)malloc(strn_length * sizeof(u8));
u8 *pos = out;
auto out = std::make_unique<u8[]>(strn_length * sizeof(u8));
u8 *pos = out.get();
while (strn_length--)
{
@@ -94,26 +72,25 @@ void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_len
}
// Copy back to our array.
memcpy(data, out, data_length);
free(out);
memcpy(data, out.get(), data_length);
}
}
bool is_hex(const char* hex_str, unsigned int str_length)
{
static const char hex_chars[] = "0123456789abcdefABCDEF";
static const char hex_chars[] = "0123456789abcdefABCDEF";
if (hex_str == NULL)
return false;
if (hex_str == NULL)
return false;
unsigned int i;
for (i = 0; i < str_length; i++)
unsigned int i;
for (i = 0; i < str_length; i++)
{
if (strchr(hex_chars, hex_str[i]) == 0)
return false;
}
return true;
return true;
}
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
@@ -146,22 +123,11 @@ void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out
bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
{
unsigned char *out = new unsigned char[key_len];
std::unique_ptr<u8> out(new u8[key_len]);
sha1_hmac(key, key_len, in, in_len, out);
sha1_hmac(key, key_len, in, in_len, out.get());
for (int i = 0; i < hash_len; i++)
{
if (out[i] != hash[i])
{
delete[] out;
return false;
}
}
delete[] out;
return true;
return std::memcmp(out.get(), hash, hash_len) == 0;
}
void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
@@ -171,24 +137,13 @@ void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
{
unsigned char *out = new unsigned char[key_len];
std::unique_ptr<u8> out(new u8[key_len]);
aes_context ctx;
aes_setkey_enc(&ctx, key, 128);
aes_cmac(&ctx, in_len, in, out);
aes_cmac(&ctx, in_len, in, out.get());
for (int i = 0; i < hash_len; i++)
{
if (out[i] != hash[i])
{
delete[] out;
return false;
}
}
delete[] out;
return true;
return std::memcmp(out.get(), hash, hash_len) == 0;
}
void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
+34 -4
View File
@@ -15,10 +15,40 @@
#include "ec.h"
// Auxiliary functions (endian swap, xor, prng and file name).
u16 swap16(u16 i);
u32 swap32(u32 i);
u64 swap64(u64 i);
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size);
inline u16 swap16(u16 i)
{
#if defined(__GNUG__)
return __builtin_bswap16(i);
#else
return _byteswap_ushort(i);
#endif
}
inline u32 swap32(u32 i)
{
#if defined(__GNUG__)
return __builtin_bswap32(i);
#else
return _byteswap_ulong(i);
#endif
}
inline u64 swap64(u64 i)
{
#if defined(__GNUG__)
return __builtin_bswap64(i);
#else
return _byteswap_uint64(i);
#endif
}
void xor_key(unsigned char *dest, const u8* src1, const u8* src2);
inline void xor_key_sse(u8* dest, const u8* src1, const u8* src2)
{
_mm_storeu_si128(&(((__m128i*)dest)[0]),
_mm_xor_si128(_mm_loadu_si128((__m128i*)src1), _mm_loadu_si128((__m128i*)src2)));
}
void prng(unsigned char *dest, int size);
char* extract_file_name(const char* file_path, char real_file_name[MAX_PATH]);
+5
View File
@@ -1,5 +1,10 @@
#include "stdafx.h"
#include "AudioDumper.h"
#include "AudioThread.h"
AudioThread::~AudioThread()
{
}
AudioDumper::AudioDumper(u16 ch)
: m_header(ch)
+1 -1
View File
@@ -3,7 +3,7 @@
class AudioThread
{
public:
virtual ~AudioThread() = default;
virtual ~AudioThread();
virtual void Play() = 0;
virtual void Open(const void* src, int size) = 0;
+57 -43
View File
@@ -1,25 +1,28 @@
#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/Memory/vm.h"
#include "CPUThread.h"
#include <mutex>
DECLARE(cpu_thread::g_threads_created){0};
DECLARE(cpu_thread::g_threads_deleted){0};
template<>
template <>
void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](cpu_flag f)
{
switch (f)
{
STR_CASE(cpu_flag::stop);
STR_CASE(cpu_flag::exit);
STR_CASE(cpu_flag::suspend);
STR_CASE(cpu_flag::ret);
STR_CASE(cpu_flag::signal);
STR_CASE(cpu_flag::dbg_global_pause);
STR_CASE(cpu_flag::dbg_global_stop);
STR_CASE(cpu_flag::dbg_pause);
STR_CASE(cpu_flag::dbg_step);
case cpu_flag::stop: return "STOP";
case cpu_flag::exit: return "EXIT";
case cpu_flag::suspend: return "s";
case cpu_flag::ret: return "ret";
case cpu_flag::signal: return "sig";
case cpu_flag::memory: return "mem";
case cpu_flag::dbg_global_pause: return "G-PAUSE";
case cpu_flag::dbg_global_stop: return "G-EXIT";
case cpu_flag::dbg_pause: return "PAUSE";
case cpu_flag::dbg_step: return "STEP";
case cpu_flag::__bitset_enum_max: break;
}
@@ -41,20 +44,12 @@ void cpu_thread::on_task()
g_tls_current_cpu_thread = this;
Emu.SendDbgCommand(DID_CREATE_THREAD, this);
std::unique_lock<named_thread> lock(*this);
// Check thread status
while (!test(state & cpu_flag::exit))
while (!test(state, cpu_flag::exit + cpu_flag::dbg_global_stop))
{
CHECK_EMU_STATUS;
// check stop status
// Check stop status
if (!test(state & cpu_flag::stop))
{
if (lock) lock.unlock();
try
{
cpu_task();
@@ -73,12 +68,6 @@ void cpu_thread::on_task()
continue;
}
if (!lock)
{
lock.lock();
continue;
}
thread_ctrl::wait();
}
}
@@ -86,38 +75,58 @@ void cpu_thread::on_task()
void cpu_thread::on_stop()
{
state += cpu_flag::exit;
lock_notify();
notify();
}
cpu_thread::~cpu_thread()
{
vm::cleanup_unlock(*this);
g_threads_deleted++;
}
cpu_thread::cpu_thread()
cpu_thread::cpu_thread(u32 id)
: id(id)
{
g_threads_created++;
}
bool cpu_thread::check_state()
{
std::unique_lock<named_thread> lock(*this, std::defer_lock);
bool cpu_sleep_called = false;
bool cpu_flag_memory = false;
while (true)
{
CHECK_EMU_STATUS; // check at least once
if (test(state, cpu_flag::memory) && state.test_and_reset(cpu_flag::memory))
{
cpu_flag_memory = true;
if (test(state & cpu_flag::exit))
if (auto& ptr = vm::g_tls_locked)
{
ptr->compare_and_swap(this, nullptr);
ptr = nullptr;
}
}
if (test(state, cpu_flag::exit + cpu_flag::dbg_global_stop))
{
return true;
}
if (!test(state & cpu_state_pause))
if (test(state & cpu_flag::signal) && state.test_and_reset(cpu_flag::signal))
{
break;
cpu_sleep_called = false;
}
if (!lock)
if (!test(state, cpu_state_pause))
{
lock.lock();
if (cpu_flag_memory) vm::passive_lock(*this);
break;
}
else if (!cpu_sleep_called)
{
cpu_sleep();
cpu_sleep_called = true;
continue;
}
@@ -140,16 +149,21 @@ bool cpu_thread::check_state()
return false;
}
void cpu_thread::test_state()
{
if (UNLIKELY(test(state)))
{
if (check_state())
{
throw cpu_flag::ret;
}
}
}
void cpu_thread::run()
{
state -= cpu_flag::stop;
lock_notify();
}
void cpu_thread::set_signal()
{
verify("cpu_flag::signal" HERE), !state.test_and_set(cpu_flag::signal);
notify(); // TODO: lock_notify?
notify();
}
std::string cpu_thread::dump() const
+18 -8
View File
@@ -8,9 +8,10 @@ enum class cpu_flag : u32
{
stop, // Thread not running (HLE, initial state)
exit, // Irreversible exit
suspend, // Thread paused
suspend, // Thread suspended
ret, // Callback return requested
signal, // Thread received a signal (HLE)
memory, // Thread must unlock memory mutex
dbg_global_pause, // Emulation paused
dbg_global_stop, // Emulation stopped
@@ -31,30 +32,39 @@ public:
virtual void on_stop() override;
virtual ~cpu_thread() override;
const id_value<> id{};
const u32 id;
cpu_thread();
cpu_thread(u32 id);
// Public thread state
atomic_t<bs_t<cpu_flag>> state{+cpu_flag::stop};
// Object associated with sleep state, possibly synchronization primitive (mutex, semaphore, etc.)
atomic_t<void*> owner{};
// Process thread state, return true if the checker must return
bool check_state();
// Process thread state
void test_state();
// Run thread
void run();
// Set cpu_flag::signal
void set_signal();
// Check thread type
u32 id_type()
{
return id >> 24;
}
// Thread stats for external observation
static atomic_t<u64> g_threads_created, g_threads_deleted;
// Print CPU state
virtual std::string dump() const;
// Thread entry point function
virtual void cpu_task() = 0;
// Callback for cpu_flag::suspend
virtual void cpu_sleep() {}
};
inline cpu_thread* get_current_cpu_thread() noexcept
+37 -33
View File
@@ -28,6 +28,7 @@ enum CellError : u32
CELL_ETIMEDOUT = 0x8001000B, // The operation is timed out
CELL_EABORT = 0x8001000C, // The operation is aborted
CELL_EFAULT = 0x8001000D, // Invalid memory access
CELL_ENOCHILD = 0x8001000E, // Process has no child(s)
CELL_ESTAT = 0x8001000F, // State of the target thread is invalid
CELL_EALIGN = 0x80010010, // Alignment is invalid.
CELL_EKRESOURCE = 0x80010011, // Shortage of the kernel resources
@@ -40,37 +41,40 @@ enum CellError : u32
CELL_ENOTMSELF = 0x80010018, // The file is not a MSELF
CELL_ESYSVER = 0x80010019, // System version error
CELL_EAUTHFATAL = 0x8001001A, // Fatal system error
CELL_EDOM = 0x8001001B,
CELL_ERANGE = 0x8001001C,
CELL_EILSEQ = 0x8001001D,
CELL_EFPOS = 0x8001001E,
CELL_EINTR = 0x8001001F,
CELL_EFBIG = 0x80010020,
CELL_EMLINK = 0x80010021,
CELL_ENFILE = 0x80010022,
CELL_ENOSPC = 0x80010023,
CELL_ENOTTY = 0x80010024,
CELL_EPIPE = 0x80010025,
CELL_EROFS = 0x80010026,
CELL_ESPIPE = 0x80010027,
CELL_E2BIG = 0x80010028,
CELL_EACCES = 0x80010029,
CELL_EBADF = 0x8001002A,
CELL_EIO = 0x8001002B,
CELL_EMFILE = 0x8001002C,
CELL_ENODEV = 0x8001002D,
CELL_ENOTDIR = 0x8001002E,
CELL_ENXIO = 0x8001002F,
CELL_EXDEV = 0x80010030,
CELL_EBADMSG = 0x80010031,
CELL_EINPROGRESS = 0x80010032,
CELL_EMSGSIZE = 0x80010033,
CELL_ENAMETOOLONG = 0x80010034,
CELL_ENOLCK = 0x80010035,
CELL_ENOTEMPTY = 0x80010036,
CELL_ENOTSUP = 0x80010037,
CELL_EFSSPECIFIC = 0x80010038,
CELL_EOVERFLOW = 0x80010039,
CELL_ENOTMOUNTED = 0x8001003A,
CELL_ENOTSDATA = 0x8001003B,
CELL_EDOM = 0x8001001B, // Math domain violation
CELL_ERANGE = 0x8001001C, // Math range violation
CELL_EILSEQ = 0x8001001D, // Illegal multi-byte sequence in input
CELL_EFPOS = 0x8001001E, // File position error
CELL_EINTR = 0x8001001F, // Syscall was interrupted
CELL_EFBIG = 0x80010020, // File too large
CELL_EMLINK = 0x80010021, // Too many links
CELL_ENFILE = 0x80010022, // File table overflow
CELL_ENOSPC = 0x80010023, // No space left on device
CELL_ENOTTY = 0x80010024, // Not a TTY
CELL_EPIPE = 0x80010025, // Broken pipe
CELL_EROFS = 0x80010026, // Read-only filesystem (write fail)
CELL_ESPIPE = 0x80010027, // Illegal seek (e.g. seek on pipe)
CELL_E2BIG = 0x80010028, // Arg list too long
CELL_EACCES = 0x80010029, // Access violation
CELL_EBADF = 0x8001002A, // Invalid file descriptor
CELL_EIO = 0x8001002B, // Filesystem mounting failed (actually IO error...EIO)
CELL_EMFILE = 0x8001002C, // Too many files open
CELL_ENODEV = 0x8001002D, // No device
CELL_ENOTDIR = 0x8001002E, // Not a directory
CELL_ENXIO = 0x8001002F, // No such device or IO
CELL_EXDEV = 0x80010030, // Cross-device link error
CELL_EBADMSG = 0x80010031, // Bad Message
CELL_EINPROGRESS = 0x80010032, // In progress
CELL_EMSGSIZE = 0x80010033, // Message size error
CELL_ENAMETOOLONG = 0x80010034, // Name too long
CELL_ENOLCK = 0x80010035, // No lock
CELL_ENOTEMPTY = 0x80010036, // Not empty
CELL_ENOTSUP = 0x80010037, // Not supported
CELL_EFSSPECIFIC = 0x80010038, // File-system specific error
CELL_EOVERFLOW = 0x80010039, // Overflow occured
CELL_ENOTMOUNTED = 0x8001003A, // Filesystem not mounted
CELL_ENOTSDATA = 0x8001003B, // Not SData
CELL_ESDKVER = 0x8001003C, // Incorrect version in sys_load_param
CELL_ENOLICDISC = 0x8001003D, // Pointer is null. Similar than 0x8001003E but with some PARAM.SFO parameter (TITLE_ID?) embedded.
CELL_ENOLICENT = 0x8001003E, // Pointer is null
};
+311 -37
View File
@@ -1,47 +1,321 @@
#include "stdafx.h"
#include "Emu/Memory/vm.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "MFC.h"
const char* get_mfc_cmd_name(u32 cmd)
template <>
void fmt_class_string<MFC>::format(std::string& out, u64 arg)
{
switch (cmd)
format_enum(out, arg, [](MFC cmd)
{
case MFC_PUT_CMD: return "PUT";
case MFC_PUTB_CMD: return "PUTB";
case MFC_PUTF_CMD: return "PUTF";
case MFC_PUTS_CMD: return "PUTS";
case MFC_PUTBS_CMD: return "PUTBS";
case MFC_PUTFS_CMD: return "PUTFS";
case MFC_PUTR_CMD: return "PUTR";
case MFC_PUTRB_CMD: return "PUTRB";
case MFC_PUTRF_CMD: return "PUTRF";
case MFC_GET_CMD: return "GET";
case MFC_GETB_CMD: return "GETB";
case MFC_GETF_CMD: return "GETF";
case MFC_GETS_CMD: return "GETS";
case MFC_GETBS_CMD: return "GETBS";
case MFC_GETFS_CMD: return "GETFS";
case MFC_PUTL_CMD: return "PUTL";
case MFC_PUTLB_CMD: return "PUTLB";
case MFC_PUTLF_CMD: return "PUTLF";
case MFC_PUTRL_CMD: return "PUTRL";
case MFC_PUTRLB_CMD: return "PUTRLB";
case MFC_PUTRLF_CMD: return "PUTRLF";
case MFC_GETL_CMD: return "GETL";
case MFC_GETLB_CMD: return "GETLB";
case MFC_GETLF_CMD: return "GETLF";
switch (cmd)
{
case MFC_PUT_CMD: return "PUT";
case MFC_PUTB_CMD: return "PUTB";
case MFC_PUTF_CMD: return "PUTF";
case MFC_PUTS_CMD: return "PUTS";
case MFC_PUTBS_CMD: return "PUTBS";
case MFC_PUTFS_CMD: return "PUTFS";
case MFC_PUTR_CMD: return "PUTR";
case MFC_PUTRB_CMD: return "PUTRB";
case MFC_PUTRF_CMD: return "PUTRF";
case MFC_GET_CMD: return "GET";
case MFC_GETB_CMD: return "GETB";
case MFC_GETF_CMD: return "GETF";
case MFC_GETS_CMD: return "GETS";
case MFC_GETBS_CMD: return "GETBS";
case MFC_GETFS_CMD: return "GETFS";
case MFC_PUTL_CMD: return "PUTL";
case MFC_PUTLB_CMD: return "PUTLB";
case MFC_PUTLF_CMD: return "PUTLF";
case MFC_PUTRL_CMD: return "PUTRL";
case MFC_PUTRLB_CMD: return "PUTRLB";
case MFC_PUTRLF_CMD: return "PUTRLF";
case MFC_GETL_CMD: return "GETL";
case MFC_GETLB_CMD: return "GETLB";
case MFC_GETLF_CMD: return "GETLF";
case MFC_GETLLAR_CMD: return "GETLLAR";
case MFC_PUTLLC_CMD: return "PUTLLC";
case MFC_PUTLLUC_CMD: return "PUTLLUC";
case MFC_PUTQLLUC_CMD: return "PUTQLLUC";
case MFC_GETLLAR_CMD: return "GETLLAR";
case MFC_PUTLLC_CMD: return "PUTLLC";
case MFC_PUTLLUC_CMD: return "PUTLLUC";
case MFC_PUTQLLUC_CMD: return "PUTQLLUC";
case MFC_SNDSIG_CMD: return "SNDSIG";
case MFC_SNDSIGB_CMD: return "SNDSIGB";
case MFC_SNDSIGF_CMD: return "SNDSIGF";
case MFC_BARRIER_CMD: return "BARRIER";
case MFC_EIEIO_CMD: return "EIEIO";
case MFC_SYNC_CMD: return "SYNC";
case MFC_SNDSIG_CMD: return "SNDSIG";
case MFC_SNDSIGB_CMD: return "SNDSIGB";
case MFC_SNDSIGF_CMD: return "SNDSIGF";
case MFC_BARRIER_CMD: return "BARRIER";
case MFC_EIEIO_CMD: return "EIEIO";
case MFC_SYNC_CMD: return "SYNC";
}
return unknown;
});
}
mfc_thread::mfc_thread()
: cpu_thread(0)
{
}
mfc_thread::~mfc_thread()
{
}
std::string mfc_thread::get_name() const
{
return "MFC Thread";
}
void mfc_thread::cpu_task()
{
vm::passive_lock(*this);
u32 no_updates = 0;
while (!m_spus.empty() || m_spuq.size() != 0)
{
// Add or remove destroyed SPU threads
while (m_spuq.size())
{
auto& thread_ptr = m_spuq[0];
// Look for deleted threads if nullptr received
for (auto it = m_spus.cbegin(); !thread_ptr && it != m_spus.cend();)
{
if (test(it->get()->state, cpu_flag::exit))
{
it = m_spus.erase(it);
}
else
{
it++;
}
}
// Add thread
if (thread_ptr)
{
m_spus.emplace_back(std::move(thread_ptr));
}
m_spuq.end_pop();
no_updates = 0;
}
test_state();
// Process SPU threads
for (const auto& thread_ptr : m_spus)
{
SPUThread& spu = *thread_ptr;
const auto proxy_size = spu.mfc_proxy.size();
const auto queue_size = spu.mfc_queue.size();
if (proxy_size)
{
const auto& cmd = spu.mfc_proxy[0];
spu.do_dma_transfer(cmd);
if (cmd.cmd & MFC_START_MASK && !spu.status.test_and_set(SPU_STATUS_RUNNING))
{
spu.run();
}
spu.mfc_proxy.end_pop();
no_updates = 0;
}
test_state();
if (queue_size)
{
auto& cmd = spu.mfc_queue[0];
if ((cmd.cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) == MFC_PUTQLLUC_CMD)
{
auto& data = vm::ps3::_ref<decltype(spu.rdata)>(cmd.eal);
const auto to_write = spu._ref<decltype(spu.rdata)>(cmd.lsa & 0x3ffff);
cmd.size = 0;
no_updates = 0;
// Store unconditionally
vm::writer_lock lock(0);
data = to_write;
vm::reservation_update(cmd.eal, 128);
vm::notify(cmd.eal, 128);
}
else if (cmd.cmd & MFC_LIST_MASK)
{
struct list_element
{
be_t<u16> sb; // Stall-and-Notify bit (0x8000)
be_t<u16> ts; // List Transfer Size
be_t<u32> ea; // External Address Low
};
if (cmd.size && (spu.ch_stall_mask & (1u << cmd.tag)) == 0)
{
cmd.lsa &= 0x3fff0;
const list_element item = spu._ref<list_element>(cmd.eal & 0x3fff8);
const u32 size = item.ts;
const u32 addr = item.ea;
if (size)
{
spu_mfc_cmd transfer;
transfer.eal = addr;
transfer.eah = 0;
transfer.lsa = cmd.lsa | (addr & 0xf);
transfer.tag = cmd.tag;
transfer.cmd = MFC(cmd.cmd & ~MFC_LIST_MASK);
transfer.size = size;
spu.do_dma_transfer(transfer);
cmd.lsa += std::max<u32>(size, 16);
}
cmd.eal += 8;
cmd.size -= 8;
no_updates = 0;
if (item.sb & 0x8000)
{
spu.ch_stall_stat.push_or(spu, 1 << cmd.tag);
const u32 evt = spu.ch_event_stat.fetch_or(SPU_EVENT_SN);
if (evt & SPU_EVENT_WAITING)
{
spu.notify();
}
else if (evt & SPU_EVENT_INTR_ENABLED)
{
spu.state += cpu_flag::suspend;
}
}
}
}
else if (LIKELY(cmd.size))
{
spu.do_dma_transfer(cmd);
cmd.size = 0;
}
else if (UNLIKELY((cmd.cmd & ~0xc) == MFC_BARRIER_CMD))
{
// TODO (MFC_BARRIER_CMD, MFC_EIEIO_CMD, MFC_SYNC_CMD)
_mm_mfence();
}
if (!cmd.size)
{
spu.mfc_queue.end_pop();
no_updates = 0;
}
}
test_state();
if (spu.ch_tag_upd)
{
// Mask incomplete transfers
u32 completed = spu.ch_tag_mask;
for (u32 i = 0; i < spu.mfc_queue.size(); i++)
{
const auto& _cmd = spu.mfc_queue[i];
if (_cmd.size)
{
if (spu.ch_tag_upd == 1)
{
completed &= ~(1u << _cmd.tag);
}
else
{
completed = 0;
break;
}
}
}
if (completed && spu.ch_tag_upd.exchange(0))
{
spu.ch_tag_stat.push(spu, completed);
no_updates = 0;
}
}
test_state();
}
if (no_updates++)
{
if (no_updates >= 3)
{
if (m_spuq.size())
{
no_updates = 0;
}
for (const auto& thread_ptr : m_spus)
{
SPUThread& spu = *thread_ptr;
if (spu.mfc_proxy.size())
{
no_updates = 0;
break;
}
if (spu.mfc_queue.size())
{
auto& cmd = spu.mfc_queue[0];
if ((cmd.cmd & MFC_LIST_MASK) == 0 || (spu.ch_stall_mask & (1u << cmd.tag)) == 0)
{
no_updates = 0;
break;
}
}
if (spu.ch_tag_upd)
{
no_updates = 0;
break;
}
}
if (no_updates)
{
vm::temporary_unlock(*this);
thread_ctrl::wait_for(100);
}
}
else
{
vm::reader_lock lock;
vm::notify_all();
}
}
}
return "UNKNOWN";
vm::passive_unlock(*this);
state += cpu_flag::stop;
}
void mfc_thread::add_spu(spu_ptr _spu)
{
while (!m_spuq.try_push(std::move(_spu)))
{
busy_wait();
continue;
}
run();
}
+50 -26
View File
@@ -1,8 +1,11 @@
#pragma once
const char* get_mfc_cmd_name(u32 cmd);
#include "Emu/CPU/CPUThread.h"
#include "Utilities/lockless.h"
enum : u32
#include <deque>
enum MFC : u8
{
MFC_PUT_CMD = 0x20, MFC_PUTB_CMD = 0x21, MFC_PUTF_CMD = 0x22,
MFC_PUTS_CMD = 0x28, MFC_PUTBS_CMD = 0x29, MFC_PUTFS_CMD = 0x2a,
@@ -64,29 +67,50 @@ enum : u32
MFC_SPU_MAX_QUEUE_SPACE = 0x10,
};
struct spu_mfc_arg_t
enum : u32
{
union
{
u64 ea;
struct
{
u32 eal;
u32 eah;
};
};
u32 lsa;
union
{
struct
{
u16 tag;
u16 size;
};
u32 size_tag;
};
MFC_DMA_TAG_STATUS_UPDATE_EVENT = 0x00000001,
MFC_DMA_TAG_CMD_STALL_NOTIFY_EVENT = 0x00000002,
MFC_DMA_QUEUE_VACANCY_EVENT = 0x00000008,
MFC_SPU_MAILBOX_WRITTEN_EVENT = 0x00000010,
MFC_DECREMENTER_EVENT = 0x00000020,
MFC_PU_INT_MAILBOX_AVAIL_EVENT = 0x00000040,
MFC_PU_MAILBOX_AVAIL_EVENT = 0x00000080,
MFC_SIGNAL_2_EVENT = 0x00000100,
MFC_SIGNAL_1_EVENT = 0x00000200,
MFC_LLR_LOST_EVENT = 0x00000400,
MFC_PRIV_ATTN_EVENT = 0x00000800,
MFG_MULTISOURCE_SYNC_EVENT = 0x00001000,
};
struct alignas(16) spu_mfc_cmd
{
MFC cmd;
u8 tag;
u16 size;
u32 lsa;
u32 eal;
u32 eah;
};
class mfc_thread : public cpu_thread
{
using spu_ptr = std::shared_ptr<class SPUThread>;
// SPU threads to poll
std::vector<spu_ptr> m_spus;
// SPU threads to enqueue
lf_mpsc<spu_ptr, 128> m_spuq;
public:
mfc_thread();
virtual ~mfc_thread() override;
virtual std::string get_name() const override;
virtual void cpu_task() override;
virtual void add_spu(spu_ptr _spu);
};
+29 -3
View File
@@ -2,6 +2,7 @@
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/lv2/sys_sync.h"
extern "C"
{
@@ -199,6 +200,7 @@ public:
// TODO: finalize
cellAdec.warning("adecEndSeq:");
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, cbArg);
lv2_obj::sleep(*this);
just_finished = true;
break;
@@ -375,11 +377,13 @@ public:
{
frame.data = nullptr; // to prevent destruction
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, cbArg);
lv2_obj::sleep(*this);
}
}
}
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, cbArg);
lv2_obj::sleep(*this);
break;
}
@@ -608,9 +612,7 @@ s32 cellAdecClose(u32 handle)
while (!adec->is_finished)
{
CHECK_EMU_STATUS;
std::this_thread::sleep_for(1ms); // hack
thread_ctrl::wait_for(1000); // hack
}
idm::remove<ppu_thread>(handle);
@@ -905,6 +907,30 @@ s32 cellAdecGetPcmItem(u32 handle, vm::pptr<CellAdecPcmItem> pcmItem)
DECLARE(ppu_module_manager::cellAdec)("cellAdec", []()
{
static ppu_static_module cell_libac3dec("cell_libac3dec");
static ppu_static_module cellAtrac3dec("cellAtrac3dec");
static ppu_static_module cellAtracXdec("cellAtracXdec");
static ppu_static_module cellCelpDec("cellCelpDec");
static ppu_static_module cellDTSdec("cellDTSdec");
static ppu_static_module cellM2AACdec("cellM2AACdec");
static ppu_static_module cellM2BCdec("cellM2BCdec");
static ppu_static_module cellM4AacDec("cellM4AacDec");
static ppu_static_module cellMP3dec("cellMP3dec");
static ppu_static_module cellTRHDdec("cellTRHDdec");
static ppu_static_module cellWMAdec("cellWMAdec");
static ppu_static_module cellDTSLBRdec("cellDTSLBRdec");
static ppu_static_module cellDDPdec("cellDDPdec");
static ppu_static_module cellM4AacDec2ch("cellM4AacDec2ch");
static ppu_static_module cellDTSHDdec("cellDTSHDdec");
static ppu_static_module cellMPL1dec("cellMPL1dec");
static ppu_static_module cellMP3Sdec("cellMP3Sdec");
static ppu_static_module cellM4AacDec2chmod("cellM4AacDec2chmod");
static ppu_static_module cellCelp8Dec("cellCelp8Dec");
static ppu_static_module cellWMAPROdec("cellWMAPROdec");
static ppu_static_module cellWMALSLdec("cellWMALSLdec");
static ppu_static_module cellDTSHDCOREdec("cellDTSHDCOREdec");
static ppu_static_module cellAtrac3multidec("cellAtrac3multidec");
REG_FUNC(cellAdec, cellAdecQueryAttr);
REG_FUNC(cellAdec, cellAdecOpen);
REG_FUNC(cellAdec, cellAdecOpenEx);
+31 -14
View File
@@ -16,8 +16,11 @@ logs::channel cellAudio("cellAudio", logs::level::notice);
cfg::bool_entry g_cfg_audio_dump_to_file(cfg::root.audio, "Dump to file");
cfg::bool_entry g_cfg_audio_convert_to_u16(cfg::root.audio, "Convert to 16 bit");
void audio_config::on_task()
void audio_config::on_init(const std::shared_ptr<void>& _this)
{
m_buffer.set(vm::alloc(AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT, vm::main));
m_indexes.set(vm::alloc(sizeof(u64) * AUDIO_PORT_COUNT, vm::main));
for (u32 i = 0; i < AUDIO_PORT_COUNT; i++)
{
ports[i].number = i;
@@ -25,6 +28,11 @@ void audio_config::on_task()
ports[i].index = m_indexes + i;
}
named_thread::on_init(_this);
}
void audio_config::on_task()
{
AudioDumper m_dump(g_cfg_audio_dump_to_file ? 2 : 0); // Init AudioDumper for 2 channels if enabled
float buf2ch[2 * BUFFER_SIZE]{}; // intermediate buffer for 2 channels
@@ -281,14 +289,13 @@ void audio_config::on_task()
// send aftermix event (normal audio event)
LV2_LOCK;
semaphore_lock lock(mutex);
for (u64 key : keys)
{
if (auto&& queue = lv2_event_queue_t::find(key))
if (auto queue = lv2_event_queue::find(key))
{
if (queue->events() < queue->size)
queue->push(lv2_lock, 0, 0, 0, 0); // TODO: check arguments
queue->send(0, 0, 0, 0); // TODO: check arguments
}
}
}
@@ -658,16 +665,26 @@ s32 cellAudioCreateNotifyEventQueue(vm::ptr<u32> id, vm::ptr<u64> key)
{
cellAudio.warning("cellAudioCreateNotifyEventQueue(id=*0x%x, key=*0x%x)", id, key);
for (u64 k = 0; k < 100; k++)
vm::var<sys_event_queue_attribute_t> attr;
attr->protocol = SYS_SYNC_FIFO;
attr->type = SYS_PPU_QUEUE;
attr->name_u64 = 0;
for (u64 i = 0; i < 100; i++)
{
const u64 key_value = 0x80004d494f323221ull + k;
// Create an event queue "bruteforcing" an available key
if (auto&& queue = lv2_event_queue_t::make(SYS_SYNC_FIFO, SYS_PPU_QUEUE, 0, key_value, 32))
{
*id = queue->id;
*key = key_value;
const u64 key_value = 0x80004d494f323221ull + i;
if (const s32 res = sys_event_queue_create(id, attr, key_value, 32))
{
if (res != CELL_EEXIST)
{
return res;
}
}
else
{
*key = key_value;
return CELL_OK;
}
}
@@ -700,7 +717,7 @@ s32 cellAudioSetNotifyEventQueue(u64 key)
return CELL_AUDIO_ERROR_NOT_INIT;
}
LV2_LOCK;
semaphore_lock lock(g_audio->mutex);
for (auto k : g_audio->keys) // check for duplicates
{
@@ -735,7 +752,7 @@ s32 cellAudioRemoveNotifyEventQueue(u64 key)
return CELL_AUDIO_ERROR_NOT_INIT;
}
LV2_LOCK;
semaphore_lock lock(g_audio->mutex);
for (auto i = g_audio->keys.begin(); i != g_audio->keys.end(); i++)
{
+13 -3
View File
@@ -125,19 +125,29 @@ class audio_config final : public named_thread
std::string get_name() const override { return "Audio Thread"; }
vm::var<char[], vm::page_allocator<vm::main>> m_buffer{ AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT };
vm::var<u64[], vm::page_allocator<vm::main>> m_indexes{ AUDIO_PORT_COUNT };
vm::ptr<char> m_buffer = vm::null;
vm::ptr<u64> m_indexes = vm::null;
u64 m_counter{};
public:
void on_init(const std::shared_ptr<void>&) override;
const u64 start_time = get_system_time();
std::array<audio_port, AUDIO_PORT_COUNT> ports;
std::vector<u64> keys;
~audio_config() noexcept = default;
semaphore<> mutex;
audio_config() = default;
~audio_config()
{
vm::dealloc_verbose_nothrow(m_buffer.addr());
vm::dealloc_verbose_nothrow(m_indexes.addr());
}
audio_port* open_port()
{
+2 -2
View File
@@ -132,11 +132,11 @@ s32 cellVideoOutGetScreenSize(u32 videoOut, vm::ptr<float> screenSize)
DECLARE(ppu_module_manager::cellAvconfExt)("cellSysutilAvconfExt", []()
{
REG_VNID(cellSysutilAvconfExt, 0x00000000, g_gamma, []
REG_VNID(cellSysutilAvconfExt, 0x00000000, g_gamma).init = []
{
// Test
*g_gamma = 1.0f;
});
};
REG_FUNC(cellSysutilAvconfExt, cellAudioOutUnregisterDevice);
REG_FUNC(cellSysutilAvconfExt, cellAudioOutGetDeviceInfo2);
+14 -1
View File
@@ -2,6 +2,7 @@
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "cellPamf.h"
#include "cellDmux.h"
@@ -143,10 +144,14 @@ class ElementaryStream
bool is_full(u32 space);
public:
static const u32 id_base = 1;
static const u32 id_step = 1;
static const u32 id_count = 1023;
ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec);
Demuxer* dmux;
const id_value<> id{};
const u32 id = idm::last_id();
const u32 memAddr;
const u32 memSize;
const u32 fidMajor;
@@ -238,6 +243,7 @@ public:
dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE;
dmuxMsg->supplementalInfo = stream.userdata;
cbFunc(*this, id, dmuxMsg, cbArg);
lv2_obj::sleep(*this);
is_working = false;
@@ -391,6 +397,7 @@ public:
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
esMsg->supplementalInfo = stream.userdata;
es.cbFunc(*this, id, es.id, esMsg, es.cbArg);
lv2_obj::sleep(*this);
}
}
else
@@ -456,6 +463,7 @@ public:
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
esMsg->supplementalInfo = stream.userdata;
es.cbFunc(*this, id, es.id, esMsg, es.cbArg);
lv2_obj::sleep(*this);
}
if (pes.has_ts)
@@ -532,6 +540,7 @@ public:
dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE;
dmuxMsg->supplementalInfo = stream.userdata;
cbFunc(*this, id, dmuxMsg, cbArg);
lv2_obj::sleep(*this);
stream = {};
@@ -620,6 +629,7 @@ public:
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
esMsg->supplementalInfo = stream.userdata;
es.cbFunc(*this, id, es.id, esMsg, es.cbArg);
lv2_obj::sleep(*this);
}
if (es.raw_data.size())
@@ -632,6 +642,7 @@ public:
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE;
esMsg->supplementalInfo = stream.userdata;
es.cbFunc(*this, id, es.id, esMsg, es.cbArg);
lv2_obj::sleep(*this);
break;
}
@@ -1362,6 +1373,8 @@ s32 cellDmuxFlushEs(u32 esHandle)
DECLARE(ppu_module_manager::cellDmux)("cellDmux", []()
{
static ppu_static_module cellDmuxPamf("cellDmuxPamf");
REG_FUNC(cellDmux, cellDmuxQueryAttr);
REG_FUNC(cellDmux, cellDmuxQueryAttr2);
REG_FUNC(cellDmux, cellDmuxOpen);
+2
View File
@@ -743,6 +743,8 @@ s32 cellFontGraphicsGetLineRGBA()
DECLARE(ppu_module_manager::cellFont)("cellFont", []()
{
static ppu_static_module cell_FreeType2("cell_FreeType2");
REG_FUNC(cellFont, cellFontSetFontsetOpenMode);
REG_FUNC(cellFont, cellFontSetFontOpenMode);
REG_FUNC(cellFont, cellFontCreateRenderer);
+102 -171
View File
@@ -4,12 +4,15 @@
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/lv2/sys_fs.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "cellFs.h"
#include "Utilities/StrUtil.h"
#include <mutex>
namespace vm { using namespace ps3; }
logs::channel cellFs("cellFs", logs::level::notice);
s32 cellFsOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<void> arg, u64 size)
@@ -206,6 +209,16 @@ s32 cellFsChmod(vm::cptr<char> path, s32 mode)
return sys_fs_chmod(path, mode);
}
s32 cellFsUtime(vm::cptr<char> path, vm::cptr<CellFsUtimbuf> timep)
{
cellFs.warning("cellFsUtime(path=%s, timep=*0x%x) -> sys_fs_utime()", path, timep);
// TODO
// Call the syscall
return sys_fs_utime(path, timep);
}
s32 cellFsGetFreeSize(vm::cptr<char> path, vm::ptr<u32> block_size, vm::ptr<u64> block_count)
{
cellFs.warning("cellFsGetFreeSize(path=%s, block_size=*0x%x, block_count=*0x%x)", path, block_size, block_count);
@@ -221,7 +234,7 @@ s32 cellFsGetDirectoryEntries(u32 fd, vm::ptr<CellFsDirectoryEntry> entries, u32
{
cellFs.warning("cellFsGetDirectoryEntries(fd=%d, entries=*0x%x, entries_size=0x%x, data_count=*0x%x)", fd, entries, entries_size, data_count);
const auto directory = idm::get<lv2_dir>(fd);
const auto directory = idm::get<lv2_fs_object, lv2_dir>(fd);
if (!directory)
{
@@ -345,7 +358,7 @@ s32 cellFsStReadInit(u32 fd, vm::cptr<CellFsRingBuffer> ringbuf)
return CELL_EINVAL;
}
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -366,7 +379,7 @@ s32 cellFsStReadFinish(u32 fd)
{
cellFs.todo("cellFsStReadFinish(fd=%d)", fd);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -382,7 +395,7 @@ s32 cellFsStReadGetRingBuf(u32 fd, vm::ptr<CellFsRingBuffer> ringbuf)
{
cellFs.todo("cellFsStReadGetRingBuf(fd=%d, ringbuf=*0x%x)", fd, ringbuf);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -398,7 +411,7 @@ s32 cellFsStReadGetStatus(u32 fd, vm::ptr<u64> status)
{
cellFs.todo("cellFsStReadGetRingBuf(fd=%d, status=*0x%x)", fd, status);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -414,7 +427,7 @@ s32 cellFsStReadGetRegid(u32 fd, vm::ptr<u64> regid)
{
cellFs.todo("cellFsStReadGetRingBuf(fd=%d, regid=*0x%x)", fd, regid);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -430,7 +443,7 @@ s32 cellFsStReadStart(u32 fd, u64 offset, u64 size)
{
cellFs.todo("cellFsStReadStart(fd=%d, offset=0x%llx, size=0x%llx)", fd, offset, size);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -446,7 +459,7 @@ s32 cellFsStReadStop(u32 fd)
{
cellFs.todo("cellFsStReadStop(fd=%d)", fd);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -462,7 +475,7 @@ s32 cellFsStRead(u32 fd, vm::ptr<u8> buf, u64 size, vm::ptr<u64> rsize)
{
cellFs.todo("cellFsStRead(fd=%d, buf=*0x%x, size=0x%llx, rsize=*0x%x)", fd, buf, size, rsize);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -478,7 +491,7 @@ s32 cellFsStReadGetCurrentAddr(u32 fd, vm::ptr<u32> addr, vm::ptr<u64> size)
{
cellFs.todo("cellFsStReadGetCurrentAddr(fd=%d, addr=*0x%x, size=*0x%x)", fd, addr, size);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -494,7 +507,7 @@ s32 cellFsStReadPutCurrentAddr(u32 fd, vm::ptr<u8> addr, u64 size)
{
cellFs.todo("cellFsStReadPutCurrentAddr(fd=%d, addr=*0x%x, size=0x%llx)", fd, addr, size);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -510,7 +523,7 @@ s32 cellFsStReadWait(u32 fd, u64 size)
{
cellFs.todo("cellFsStReadWait(fd=%d, size=0x%llx)", fd, size);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -526,7 +539,7 @@ s32 cellFsStReadWaitCallback(u32 fd, u64 size, vm::ptr<void(s32 xfd, u64 xsize)>
{
cellFs.todo("cellFsStReadWaitCallback(fd=%d, size=0x%llx, func=*0x%x)", fd, size, func);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -538,113 +551,6 @@ s32 cellFsStReadWaitCallback(u32 fd, u64 size, vm::ptr<void(s32 xfd, u64 xsize)>
return CELL_OK;
}
bool sdata_check(u32 version, u32 flags, u64 filesizeInput, u64 filesizeTmp)
{
if (version > 4 || flags & 0x7EFFFFC0){
printf("ERROR: unknown version");
return false;
}
if ((version == 1 && (flags & 0x7FFFFFFE)) ||
(version == 2 && (flags & 0x7EFFFFC0))){
printf("ERROR: unknown or unsupported type");
return false;
}
if (filesizeTmp > filesizeInput){
printf("ERROR: input file size is too short.");
return false;
}
if (!(flags & 0x80000000)){
printf("ERROR: cannot extract finalized edata.");
return false;
}
return true;
}
s32 sdata_unpack(const std::string& packed_file, const std::string& unpacked_file)
{
fs::file packed_stream(vfs::get(packed_file));
fs::file unpacked_stream(vfs::get(unpacked_file), fs::rewrite);
if (!packed_stream)
{
cellFs.error("File '%s' not found!", packed_file);
return CELL_ENOENT;
}
if (!unpacked_stream)
{
cellFs.error("File '%s' couldn't be created!", unpacked_file);
return CELL_ENOENT;
}
char buffer[10200];
packed_stream.read(buffer, 256);
u32 format = *(be_t<u32>*)&buffer[0];
if (format != 0x4E504400) // "NPD\x00"
{
cellFs.error("Illegal format. Expected 0x4E504400, but got 0x%08x", format);
return CELL_EFSSPECIFIC;
}
u32 version = *(be_t<u32>*)&buffer[0x04];
u32 flags = *(be_t<u32>*)&buffer[0x80];
u32 blockSize = *(be_t<u32>*)&buffer[0x84];
u64 filesizeOutput = *(be_t<u64>*)&buffer[0x88];
u64 filesizeInput = packed_stream.size();
u32 blockCount = (u32)((filesizeOutput + blockSize - 1) / blockSize);
// SDATA file is compressed
if (flags & 0x1)
{
cellFs.warning("cellFsSdataOpen: Compressed SDATA files are not supported yet.");
return CELL_EFSSPECIFIC;
}
// SDATA file is NOT compressed
else
{
u32 t1 = (flags & 0x20) ? 0x20 : 0x10;
u32 startOffset = (blockCount * t1) + 0x100;
u64 filesizeTmp = (filesizeOutput + 0xF) & 0xFFFFFFF0 + startOffset;
if (!sdata_check(version, flags, filesizeInput, filesizeTmp))
{
cellFs.error("cellFsSdataOpen: Wrong header information.");
return CELL_EFSSPECIFIC;
}
if (flags & 0x20)
{
packed_stream.seek(0x100);
}
else
{
packed_stream.seek(startOffset);
}
for (u32 i = 0; i < blockCount; i++)
{
if (flags & 0x20)
{
packed_stream.seek(t1, fs::seek_cur);
}
if (!(blockCount - i - 1))
{
blockSize = (u32)(filesizeOutput - i * blockSize);
}
packed_stream.read(buffer + 256, blockSize);
unpacked_stream.write(buffer + 256, blockSize);
}
}
return CELL_OK;
}
s32 cellFsSdataOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<void> arg, u64 size)
{
cellFs.notice("cellFsSdataOpen(path=%s, flags=%#o, fd=*0x%x, arg=*0x%x, size=0x%llx)", path, flags, fd, arg, size);
@@ -654,33 +560,52 @@ s32 cellFsSdataOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<vo
return CELL_EINVAL;
}
return cellFsOpen(path, CELL_FS_O_RDONLY, fd, vm::make_var<be_t<u32>[2]>({ 0x180, 0x10 }), 8);
// Don't implement sdata decryption in this function, it should be done in sys_fs_open() syscall or somewhere else
/*
std::string suffix = path.substr(path.length() - 5, 5);
if (suffix != ".sdat" && suffix != ".SDAT")
return CELL_ENOTSDATA;
std::string::size_type last_slash = path.rfind('/'); //TODO: use a filesystem library to solve this more robustly
last_slash = last_slash == std::string::npos ? 0 : last_slash+1;
std::string unpacked_path = "/dev_hdd1/"+path.substr(last_slash,path.length()-last_slash)+".unpacked";
s32 ret = sdata_unpack(path, unpacked_path);
if (ret) return ret;
fd = idm::GetNewID(Emu.GetVFS().OpenFile(unpacked_path, vfsRead), TYPE_FS_FILE);
return CELL_OK;
*/
return cellFsOpen(path, CELL_FS_O_RDONLY, fd, vm::make_var<be_t<u32>[2]>({0x180, 0x10}), 8);
}
s32 cellFsSdataOpenByFd(u32 mself_fd, s32 flags, vm::ptr<u32> sdata_fd, u64 offset, vm::cptr<void> arg, u64 size)
{
cellFs.todo("cellFsSdataOpenByFd(mself_fd=0x%x, flags=%#o, sdata_fd=*0x%x, offset=0x%llx, arg=*0x%x, size=0x%llx)", mself_fd, flags, sdata_fd, offset, arg, size);
cellFs.notice("cellFsSdataOpenByFd(mself_fd=0x%x, flags=%#o, sdata_fd=*0x%x, offset=0x%llx, arg=*0x%x, size=0x%llx)", mself_fd, flags, sdata_fd, offset, arg, size);
// TODO:
if (!sdata_fd)
{
return CELL_EFAULT;
}
*sdata_fd = -1;
if (mself_fd < 3 || mself_fd > 255)
{
return CELL_EBADF;
}
if (flags)
{
return CELL_EINVAL;
}
vm::var<lv2_file_op_09> ctrl;
ctrl->_vtable = vm::cast(0xfa880000); // Intentionally wrong (provide correct vtable if necessary)
ctrl->op = 0x80000009;
ctrl->fd = mself_fd;
ctrl->offset = offset;
ctrl->_vtabl2 = vm::cast(0xfa880020);
ctrl->arg1 = 0x180;
ctrl->arg2 = 0x10;
ctrl->arg_ptr = arg.addr();
ctrl->arg_size = u32(size);
if (const s32 rc = sys_fs_fcntl(mself_fd, 0x80000009, ctrl, 0x40))
{
return rc;
}
if (const s32 rc = ctrl->out_code)
{
return rc;
}
*sdata_fd = ctrl->out_fd;
return CELL_OK;
}
@@ -710,7 +635,7 @@ struct fs_aio_thread : ppu_thread
s32 error = CELL_OK;
u64 result = 0;
const auto file = idm::get<lv2_file>(aio->fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(aio->fd);
if (!file || (type == 1 && file->flags & CELL_FS_O_WRONLY) || (type == 2 && !(file->flags & CELL_FS_O_ACCMODE)))
{
@@ -730,6 +655,7 @@ struct fs_aio_thread : ppu_thread
}
func(*this, aio, error, xid, result);
lv2_obj::sleep(*this);
}
}
};
@@ -737,12 +663,6 @@ struct fs_aio_thread : ppu_thread
struct fs_aio_manager
{
std::shared_ptr<fs_aio_thread> thread;
fs_aio_manager()
: thread(idm::make_ptr<ppu_thread, fs_aio_thread>("FS AIO Thread", 500))
{
thread->run();
}
};
s32 cellFsAioInit(vm::cptr<char> mount_point)
@@ -750,7 +670,13 @@ s32 cellFsAioInit(vm::cptr<char> mount_point)
cellFs.warning("cellFsAioInit(mount_point=%s)", mount_point);
// TODO: create AIO thread (if not exists) for specified mount point
fxm::get_always<fs_aio_manager>();
const auto m = fxm::make<fs_aio_manager>();
if (m)
{
m->thread = idm::make_ptr<ppu_thread, fs_aio_thread>("FS AIO Thread", 500);
m->thread->run();
}
return CELL_OK;
}
@@ -772,9 +698,14 @@ s32 cellFsAioRead(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
// TODO: detect mount point and send AIO request to the AIO thread of this mount point
const s32 xid = (*id = ++g_fs_aio_id);
const auto m = fxm::get<fs_aio_manager>();
const auto m = fxm::get_always<fs_aio_manager>();
if (!m)
{
return CELL_ENXIO;
}
const s32 xid = (*id = ++g_fs_aio_id);
m->thread->cmd_list
({
@@ -782,7 +713,7 @@ s32 cellFsAioRead(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
{ aio, func },
});
m->thread->lock_notify();
m->thread->notify();
return CELL_OK;
}
@@ -793,9 +724,14 @@ s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
// TODO: detect mount point and send AIO request to the AIO thread of this mount point
const s32 xid = (*id = ++g_fs_aio_id);
const auto m = fxm::get<fs_aio_manager>();
const auto m = fxm::get_always<fs_aio_manager>();
if (!m)
{
return CELL_ENXIO;
}
const s32 xid = (*id = ++g_fs_aio_id);
m->thread->cmd_list
({
@@ -803,7 +739,7 @@ s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
{ aio, func },
});
m->thread->lock_notify();
m->thread->notify();
return CELL_OK;
}
@@ -828,7 +764,7 @@ s32 cellFsSetIoBufferFromDefaultContainer(u32 fd, u32 buffer_size, u32 page_type
{
cellFs.todo("cellFsSetIoBufferFromDefaultContainer(fd=%d, buffer_size=%d, page_type=%d)", fd, buffer_size, page_type);
const auto file = idm::get<lv2_file>(fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
@@ -838,11 +774,6 @@ s32 cellFsSetIoBufferFromDefaultContainer(u32 fd, u32 buffer_size, u32 page_type
return CELL_OK;
}
s32 cellFsUtime()
{
fmt::throw_exception("Unimplemented" HERE);
}
s32 cellFsArcadeHddSerialNumber()
{
fmt::throw_exception("Unimplemented" HERE);
@@ -911,24 +842,24 @@ DECLARE(ppu_module_manager::cellFs)("sys_fs", []()
REG_FUNC(sys_fs, cellFsOpen);
REG_FUNC(sys_fs, cellFsSdataOpen);
REG_FUNC(sys_fs, cellFsSdataOpenByFd);
REG_FUNC(sys_fs, cellFsRead, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsWrite, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsClose, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsRead).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsWrite).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsClose).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsOpendir);
REG_FUNC(sys_fs, cellFsReaddir, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsClosedir, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsReaddir).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsClosedir).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsStat);
REG_FUNC(sys_fs, cellFsFstat, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsFstat).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsMkdir);
REG_FUNC(sys_fs, cellFsRename);
REG_FUNC(sys_fs, cellFsChmod);
REG_FUNC(sys_fs, cellFsFsync);
REG_FUNC(sys_fs, cellFsRmdir);
REG_FUNC(sys_fs, cellFsUnlink);
REG_FUNC(sys_fs, cellFsLseek, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsFtruncate, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsLseek).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsFtruncate).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsTruncate);
REG_FUNC(sys_fs, cellFsFGetBlockSize, MFF_PERFECT);
REG_FUNC(sys_fs, cellFsFGetBlockSize).flags = MFF_PERFECT;
REG_FUNC(sys_fs, cellFsAioInit);
REG_FUNC(sys_fs, cellFsAioFinish);
REG_FUNC(sys_fs, cellFsAioRead);
@@ -961,7 +892,7 @@ DECLARE(ppu_module_manager::cellFs)("sys_fs", []()
REG_FUNC(sys_fs, cellFsAllocateFileAreaByFdWithInitialData);
REG_FUNC(sys_fs, cellFsTruncate2);
REG_FUNC(sys_fs, cellFsChangeFileSizeWithoutAllocation);
REG_FUNC(sys_fs, cellFsAllocateFileAreaWithoutZeroFill, MFF_FORCED_HLE);
REG_FUNC(sys_fs, cellFsAllocateFileAreaWithoutZeroFill).flags = MFF_FORCED_HLE;
REG_FUNC(sys_fs, cellFsChangeFileSizeByFdWithoutAllocation);
REG_FUNC(sys_fs, cellFsSetDiscReadRetrySetting);
REG_FUNC(sys_fs, cellFsRegisterConversionCallback);
+80 -31
View File
@@ -179,9 +179,12 @@ s32 cellHddGameCheck2()
fmt::throw_exception("Unimplemented" HERE);
}
s32 cellHddGameGetSizeKB()
s32 cellHddGameGetSizeKB(vm::ptr<u32> size)
{
fmt::throw_exception("Unimplemented" HERE);
cellGame.todo("cellHddGameGetSizeKB(size=*0x%x)", size);
*size = 0;
return CELL_OK;
}
s32 cellHddGameSetSystemVer()
@@ -197,11 +200,9 @@ s32 cellHddGameExitBroken()
s32 cellGameDataGetSizeKB(vm::ptr<u32> size)
{
cellGame.warning("cellGameDataGetSizeKB(): Unimplemented, returning 0");
if (size)
{
*size = 0;
}
cellGame.todo("cellGameDataGetSizeKB(size=*0x%x)", size);
*size = 0;
return CELL_OK;
}
@@ -246,7 +247,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
return CELL_GAME_ERROR_BUSY;
}
}
else if (category == "HG")
else if (category == "AP" || category == "AV" || category == "HG")
{
*type = CELL_GAME_GAMETYPE_HDD;
*attributes = 0; // TODO
@@ -404,6 +405,8 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
{
cellGame.error("cellGameDataCheckCreate2(version=0x%x, dirName=%s, errDialog=0x%x, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container);
//older sdk. it might not care about game type.
if (version != CELL_GAMEDATA_VERSION_CURRENT || errDialog > 1)
{
return CELL_GAMEDATA_ERROR_PARAM;
@@ -413,21 +416,15 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
const std::string& dir = "/dev_hdd0/game/"s + dirName.get_ptr();
if (!fs::is_dir(vfs::get(dir)))
{
cellGame.todo("cellGameDataCheckCreate2(): should create directory '%s'", dir);
// TODO: create data
return CELL_OK;
}
vm::var<CellGameDataCBResult> cbResult;
vm::var<CellGameDataStatGet> cbGet;
vm::var<CellGameDataStatSet> cbSet;
cbGet->isNewData = fs::is_dir(vfs::get(dir)) ? CELL_GAMEDATA_ISNEWDATA_NO : CELL_GAMEDATA_ISNEWDATA_YES;
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
cbGet->hddFreeSizeKB = 40000000; //40 GB
cbGet->isNewData = CELL_GAMEDATA_ISNEWDATA_NO;
strcpy_trunc(cbGet->contentInfoPath, dir);
strcpy_trunc(cbGet->gameDataPath, dir + "/USRDIR");
@@ -447,23 +444,55 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
strcpy_trunc(cbGet->getParam.dataVersion, psf::get_string(sfo, "APP_VER", ""));
strcpy_trunc(cbGet->getParam.titleId, psf::get_string(sfo, "TITLE_ID", ""));
strcpy_trunc(cbGet->getParam.title, psf::get_string(sfo, "TITLE", ""));
// TODO: write lang titles
for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++)
{
strcpy_trunc(cbGet->getParam.titleLang[i], psf::get_string(sfo, fmt::format("TITLE_%02d", i)));
}
funcStat(ppu, cbResult, cbGet, cbSet);
if (cbSet->setParam)
{
// TODO: write PARAM.SFO from cbSet
cellGame.todo("cellGameDataCheckCreate2(): writing PARAM.SFO parameters (addr=0x%x)", cbSet->setParam);
}
switch ((s32)cbResult->result)
{
case CELL_GAMEDATA_CBRESULT_OK_CANCEL:
// TODO: do not process game data
// TODO: do not process game data(directory)
cellGame.warning("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_OK_CANCEL");
return CELL_OK;
case CELL_GAMEDATA_CBRESULT_OK:
case CELL_GAMEDATA_CBRESULT_OK:
//game confirmed that it wants to create directory
if (!fs::is_dir(vfs::get(dir + "/USRDIR")) && !fs::create_path(vfs::get(dir + "/USRDIR")))
{
cellGame.error("cellGameDataCheckCreate2(): folder creation failed");
return CELL_GAME_ERROR_NOSPACE; //don't know which error. picked one at random
}
if (cbSet->setParam)
{
const auto vdir = vfs::get(dir);
//older SDK does not define not settable values, hopefully it doesn't just change some values(overwrite)
psf::registry sfo_write =
{
{ "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, cbSet->setParam->titleId) },
{ "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, cbSet->setParam->title) },
{ "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, cbSet->setParam->dataVersion) },
{ "PARENTAL_LEVEL", cbSet->setParam->parentalLevel.value() }
};
//sfo_write.emplace("PARENTAL_LEVEL", cbSet->setParam->parentalLevel.value()); // I don't care about age restrictions.
for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++)
{
sfo_write.emplace(fmt::format("TITLE_%02d", i), psf::string(CELL_GAME_SYSP_TITLE_SIZE, cbSet->setParam->titleLang[i]));
}
if (!fs::is_dir(vdir))
{
cellGame.fatal("directory where param.sfo is to be created does not exist");
return CELL_GAME_ERROR_INTERNAL;
}
psf::save_object(fs::file(vdir + "/PARAM.SFO", fs::rewrite), sfo_write);
}
return CELL_OK;
case CELL_GAMEDATA_CBRESULT_ERR_NOSPACE: // TODO: process errors, error message and needSizeKB result
@@ -600,6 +629,7 @@ static const char* get_param_string_key(s32 id)
case CELL_GAME_PARAMID_TITLE_POLISH: return "TITLE_16";
case CELL_GAME_PARAMID_TITLE_PORTUGUESE_BRAZIL: return "TITLE_17";
case CELL_GAME_PARAMID_TITLE_ENGLISH_UK: return "TITLE_18";
case CELL_GAME_PARAMID_TITLE_TURKISH: return "TITLE_19";
case CELL_GAME_PARAMID_TITLE_ID: return "TITLE_ID";
case CELL_GAME_PARAMID_VERSION: return "VERSION";
@@ -669,9 +699,11 @@ error_code cellGameSetParamString(s32 id, vm::cptr<char> buf)
return CELL_OK;
}
s32 cellGameGetSizeKB()
s32 cellGameGetSizeKB(vm::ptr<s32> size)
{
UNIMPLEMENTED_FUNC(cellGame);
cellGame.todo("cellGameGetSizeKB(size=*0x%x)", size);
*size = 0;
return CELL_OK;
}
@@ -735,8 +767,7 @@ error_code cellGameContentErrorDialog(s32 type, s32 errNeedSizeKB, vm::cptr<char
while (!result)
{
CHECK_EMU_STATUS;
std::this_thread::sleep_for(1ms);
thread_ctrl::wait_for(1000);
}
return CELL_OK;
@@ -755,9 +786,27 @@ s32 cellGameThemeInstallFromBuffer()
}
s32 cellDiscGameGetBootDiscInfo()
s32 cellDiscGameGetBootDiscInfo(vm::ptr<CellDiscGameSystemFileParam> getParam)
{
UNIMPLEMENTED_FUNC(cellGame);
cellGame.warning("cellDiscGameGetBootDiscInfo(getParam=*0x%x)", getParam);
// This is also called by non-disc games, see NPUB90029
const std::string dir = "/dev_bdvd/PS3_GAME"s;
if (!fs::is_dir(vfs::get(dir)))
{
// Not a disc game. TODO: Fetch PARAM.SFO from proper game dir
cellGame.warning("cellDiscGameGetBootDiscInfo(): directory '%s' not found", dir);
getParam->parentalLevel = 0;
strcpy_trunc(getParam->titleId, "0");
return CELL_OK;
}
const auto& psf = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO")));
if (psf.count("PARENTAL_LEVEL") != 0) getParam->parentalLevel = psf.at("PARENTAL_LEVEL").as_integer();
if (psf.count("TITLE_ID") != 0) strcpy_trunc(getParam->titleId, psf.at("TITLE_ID").as_string());
return CELL_OK;
}
+11 -2
View File
@@ -104,8 +104,9 @@ enum
CELL_GAME_PARAMID_TITLE_DANISH = 16,
CELL_GAME_PARAMID_TITLE_NORWEGIAN = 17,
CELL_GAME_PARAMID_TITLE_POLISH = 18,
CELL_GAME_PARAMID_TITLE_PORTUGUESE_BRAZIL = 19,
CELL_GAME_PARAMID_TITLE_ENGLISH_UK = 20,
CELL_GAME_PARAMID_TITLE_PORTUGUESE_BRAZIL = 19, // FW 4.00
CELL_GAME_PARAMID_TITLE_ENGLISH_UK = 20, // FW 4.00
CELL_GAME_PARAMID_TITLE_TURKISH = 21, // FW 4.30
CELL_GAME_PARAMID_TITLE_ID = 100,
CELL_GAME_PARAMID_VERSION = 101,
CELL_GAME_PARAMID_PS3_SYSTEM_VER = 105,
@@ -178,6 +179,8 @@ enum // old consts
CELL_GAMEDATA_ERRDIALOG_NONE = 0,
CELL_GAMEDATA_ERRDIALOG_ALWAYS = 1,
CELL_DISCGAME_SYSP_TITLEID_SIZE=10,
};
struct CellGameDataSystemFileParam
@@ -192,6 +195,12 @@ struct CellGameDataSystemFileParam
be_t<u32> attribute;
char reserved2[256];
};
struct CellDiscGameSystemFileParam {
char titleId[CELL_DISCGAME_SYSP_TITLEID_SIZE];
char reserved0[2];
be_t<u32> parentalLevel;
char reserved1[16];
};
struct CellGameDataStatGet
{
+6 -3
View File
@@ -29,14 +29,17 @@ s32 cellGameGetHomeDataImportPath()
fmt::throw_exception("Unimplemented" HERE);
}
s32 cellGameGetHomeLaunchOptionPath()
s32 cellGameGetHomeLaunchOptionPath(vm::ptr<char> commonPath, vm::ptr<char> personalPath)
{
fmt::throw_exception("Unimplemented" HERE);
cellGameExec.todo("cellGameGetHomeLaunchOptionPath(commonPath=%s, personalPath=%s)", commonPath, personalPath);
// TODO: PlayStation home is not supported atm.
return CELL_GAME_ERROR_NOAPP;
}
s32 cellGameGetBootGameInfo(vm::ptr<u32> type, vm::ptr<char> dirName, vm::ptr<u32> execData)
{
cellGameExec.todo("cellGameGetBootGameInfo(type=*0x%x, dirName=*0x%x, execData=*0x%x)", type, dirName, execData);
cellGameExec.todo("cellGameGetBootGameInfo(type=*0x%x, dirName=%s, execData=*0x%x)", type, dirName, execData);
// TODO: Support more boot types
*type = CELL_GAME_GAMETYPE_SYS;
+26 -15
View File
@@ -12,8 +12,8 @@
logs::channel cellGcmSys("cellGcmSys", logs::level::notice);
extern s32 cellGcmCallback(vm::ptr<CellGcmContextData> context, u32 count);
extern void ppu_register_function_at(u32 addr, ppu_function_t ptr);
extern s32 cellGcmCallback(ppu_thread& ppu, vm::ptr<CellGcmContextData> context, u32 count);
extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr);
const u32 tiled_pitches[] = {
0x00000000, 0x00000200, 0x00000300, 0x00000400,
@@ -222,13 +222,13 @@ u32 cellGcmGetControlRegister()
u32 cellGcmGetDefaultCommandWordSize()
{
cellGcmSys.trace("cellGcmGetDefaultCommandWordSize()");
return 0x400;
return gcm_info.command_size;
}
u32 cellGcmGetDefaultSegmentWordSize()
{
cellGcmSys.trace("cellGcmGetDefaultSegmentWordSize()");
return 0x100;
return gcm_info.segment_size;
}
s32 cellGcmInitDefaultFifoMode(s32 mode)
@@ -385,7 +385,7 @@ s32 _cellGcmInitBody(vm::pptr<CellGcmContextData> context, u32 cmdSize, u32 ioSi
vm::write32(gcm_info.context_addr + 0x44, 0xabadcafe);
vm::write32(gcm_info.context_addr + 0x48, ppu_instructions::HACK(FIND_FUNC(cellGcmCallback)));
vm::write32(gcm_info.context_addr + 0x4c, ppu_instructions::BLR());
ppu_register_function_at(gcm_info.context_addr + 0x48, BIND_FUNC(cellGcmCallback));
ppu_register_function_at(gcm_info.context_addr + 0x48, 8, BIND_FUNC(cellGcmCallback));
vm::_ref<CellGcmContextData>(gcm_info.context_addr) = current_context;
context->set(gcm_info.context_addr);
@@ -1134,9 +1134,22 @@ void cellGcmSetDefaultCommandBuffer()
vm::write32(fxm::get<GSRender>()->ctxt_addr, gcm_info.context_addr);
}
s32 cellGcmSetDefaultCommandBufferAndSegmentWordSize()
s32 cellGcmSetDefaultCommandBufferAndSegmentWordSize(u32 bufferSize, u32 segmentSize)
{
fmt::throw_exception("Unimplemented" HERE);
cellGcmSys.warning("cellGcmSetDefaultCommandBufferAndSegmentWordSize(bufferSize = 0x%x, segmentSize = 0x%x)", bufferSize, segmentSize);
const auto& put = vm::_ref<CellGcmControl>(gcm_info.control_addr).put;
const auto& get = vm::_ref<CellGcmControl>(gcm_info.control_addr).get;
if (put != 0x1000 || get != 0x1000 || bufferSize < segmentSize * 2)
{
return CELL_GCM_ERROR_FAILURE;
}
gcm_info.command_size = bufferSize;
gcm_info.segment_size = segmentSize;
return CELL_OK;
}
//------------------------------------------------------------------------
@@ -1285,12 +1298,12 @@ static bool isInCommandBufferExcept(u32 getPos, u32 bufferBegin, u32 bufferEnd)
return true;
}
s32 cellGcmCallback(vm::ptr<CellGcmContextData> context, u32 count)
s32 cellGcmCallback(ppu_thread& ppu, vm::ptr<CellGcmContextData> context, u32 count)
{
cellGcmSys.trace("cellGcmCallback(context=*0x%x, count=0x%x)", context, count);
auto& ctrl = vm::_ref<CellGcmControl>(gcm_info.control_addr);
const std::chrono::time_point<std::chrono::system_clock> enterWait = std::chrono::system_clock::now();
// Flush command buffer (ie allow RSX to read up to context->current)
ctrl.put.exchange(getOffsetFromAddress(context->current.addr()));
@@ -1304,16 +1317,14 @@ s32 cellGcmCallback(vm::ptr<CellGcmContextData> context, u32 count)
context->end.set(newCommandBuffer.second);
// Wait for rsx to "release" the new command buffer
while (!Emu.IsStopped())
while (true)
{
u32 getPos = ctrl.get.load();
if (isInCommandBufferExcept(getPos, newCommandBuffer.first, newCommandBuffer.second))
break;
std::chrono::time_point<std::chrono::system_clock> waitPoint = std::chrono::system_clock::now();
long long elapsedTime = std::chrono::duration_cast<std::chrono::seconds>(waitPoint - enterWait).count();
if (elapsedTime > 0)
cellGcmSys.error("Has wait for more than a second for command buffer to be released by RSX");
std::this_thread::yield();
ppu.test_state();
busy_wait();
}
return CELL_OK;
+4 -4
View File
@@ -61,7 +61,7 @@ s32 cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc src, POpe
if (!file_s) return CELL_GIFDEC_ERROR_OPEN_FILE;
current_subHandle.fileSize = file_s.size();
current_subHandle.fd = idm::make<lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
break;
}
}
@@ -97,7 +97,7 @@ s32 cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PInfo inf
case CELL_GIFDEC_FILE:
{
auto file = idm::get<lv2_file>(fd);
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
file->file.seek(0);
file->file.read(buffer, sizeof(buffer));
break;
@@ -181,7 +181,7 @@ s32 cellGifDecDecodeData(PMainHandle mainHandle, PSubHandle subHandle, vm::ptr<u
case CELL_GIFDEC_FILE:
{
auto file = idm::get<lv2_file>(fd);
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
file->file.seek(0);
file->file.read(gif.get(), fileSize);
break;
@@ -283,7 +283,7 @@ s32 cellGifDecClose(PMainHandle mainHandle, PSubHandle subHandle)
{
cellGifDec.warning("cellGifDecClose(mainHandle=*0x%x, subHandle=*0x%x)", mainHandle, subHandle);
idm::remove<lv2_file>(subHandle->fd);
idm::remove<lv2_fs_object, lv2_file>(subHandle->fd);
vm::dealloc(subHandle.addr());
+4 -4
View File
@@ -51,7 +51,7 @@ s32 cellJpgDecOpen(u32 mainHandle, vm::ptr<u32> subHandle, vm::ptr<CellJpgDecSrc
if (!file_s) return CELL_JPGDEC_ERROR_OPEN_FILE;
current_subHandle.fileSize = file_s.size();
current_subHandle.fd = idm::make<lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
break;
}
}
@@ -78,7 +78,7 @@ s32 cellJpgDecClose(u32 mainHandle, u32 subHandle)
return CELL_JPGDEC_ERROR_FATAL;
}
idm::remove<lv2_file>(subHandle_data->fd);
idm::remove<lv2_fs_object, lv2_file>(subHandle_data->fd);
idm::remove<CellJpgDecSubHandle>(subHandle);
return CELL_OK;
@@ -110,7 +110,7 @@ s32 cellJpgDecReadHeader(u32 mainHandle, u32 subHandle, vm::ptr<CellJpgDecInfo>
case CELL_JPGDEC_FILE:
{
auto file = idm::get<lv2_file>(fd);
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
file->file.seek(0);
file->file.read(buffer.get(), fileSize);
break;
@@ -189,7 +189,7 @@ s32 cellJpgDecDecodeData(u32 mainHandle, u32 subHandle, vm::ptr<u8> data, vm::cp
case CELL_JPGDEC_FILE:
{
auto file = idm::get<lv2_file>(fd);
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
file->file.seek(0);
file->file.read(jpg.get(), fileSize);
break;
+4
View File
@@ -109,6 +109,10 @@ struct CellJpgDecDataOutInfo
// Custom structs
struct CellJpgDecSubHandle
{
static const u32 id_base = 1;
static const u32 id_step = 1;
static const u32 id_count = 1023;
u32 fd;
u64 fileSize;
CellJpgDecInfo info;
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -52,20 +52,26 @@ enum
L10N_CODEPAGE_863,
L10N_CODEPAGE_866,
L10N_CODEPAGE_932,
L10N_SHIFT_JIS,
L10N_CODEPAGE_936,
L10N_GBK,
L10N_CODEPAGE_949,
L10N_UHC,
L10N_CODEPAGE_950,
L10N_BIG5,
L10N_CODEPAGE_1251,
L10N_CODEPAGE_1252,
L10N_EUC_CN,
L10N_EUC_JP,
L10N_EUC_KR,
L10N_ISO_2022_JP,
L10N_JIS,
L10N_ARIB,
L10N_HZ,
L10N_GB18030,
L10N_RIS_506,
//FW 3.10 and above
L10N_MUSIC_SHIFT_JIS,
//FW 3.10 and below
L10N_CODEPAGE_852,
L10N_CODEPAGE_1250,
L10N_CODEPAGE_737,
+10 -5
View File
@@ -10,9 +10,8 @@
extern logs::channel cellSysutil;
s32 cellMsgDialogOpen()
MsgDialogBase::~MsgDialogBase()
{
fmt::throw_exception("Unimplemented" HERE);
}
s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
@@ -114,13 +113,19 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialog
while (!result)
{
CHECK_EMU_STATUS;
std::this_thread::sleep_for(1ms);
thread_ctrl::wait_for(1000);
}
return CELL_OK;
}
s32 cellMsgDialogOpen(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
{
//Note: This function needs proper implementation, solve first argument "type" conflict with MsgDialogOpen2 in cellMsgDialog.h.
cellSysutil.todo("cellMsgDialogOpen(type=0x%x, msgString=%s, callback=*0x%x, userData=*0x%x, extParam=*0x%x)", type, msgString, callback, userData, extParam);
return cellMsgDialogOpen2(type, msgString, callback, userData, extParam);
}
s32 cellMsgDialogOpenErrorCode(ppu_thread& ppu, u32 errorCode, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
{
cellSysutil.warning("cellMsgDialogOpenErrorCode(errorCode=0x%x, callback=*0x%x, userData=*0x%x, extParam=*0x%x)", errorCode, callback, userData, extParam);
@@ -264,7 +269,7 @@ s32 cellMsgDialogProgressBarSetMsg(u32 progressBarIndex, vm::cptr<char> msgStrin
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
}
if (progressBarIndex >= dlg->type.progress_bar_count)
if (progressBarIndex >= dlg->type.progress_bar_count || !msgString)
{
return CELL_MSGDIALOG_ERROR_PARAM;
}
+1 -1
View File
@@ -80,7 +80,7 @@ public:
std::function<void(s32 status)> on_close;
virtual ~MsgDialogBase() = default;
virtual ~MsgDialogBase();
virtual void Create(const std::string& msg) = 0;
virtual void CreateOsk(const std::string& msg, char16_t* osk_text) = 0;
virtual void ProgressBarSetMsg(u32 progressBarIndex, const std::string& msg) = 0;
+42 -21
View File
@@ -16,97 +16,116 @@ struct music2_t
s32 cellMusicGetSelectionContext()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicSetSelectionContext2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicSetVolume2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicGetContentsId()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicSetSelectionContext()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicInitialize2SystemWorkload()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicGetPlaybackStatus2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicGetContentsId2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicFinalize()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicInitializeSystemWorkload()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicInitialize()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicFinalize2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicGetSelectionContext2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicGetVolume()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicGetPlaybackStatus()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicSetPlaybackCommand2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicSetPlaybackCommand()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicSelectContents2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicSelectContents()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicInitialize2(s32 mode, s32 spuPriority, vm::ptr<CellMusic2Callback> func, vm::ptr<void> userData)
@@ -134,12 +153,14 @@ s32 cellMusicInitialize2(s32 mode, s32 spuPriority, vm::ptr<CellMusic2Callback>
s32 cellMusicSetVolume()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
s32 cellMusicGetVolume2()
{
fmt::throw_exception("Unimplemented" HERE);
UNIMPLEMENTED_FUNC(cellMusic);
return CELL_OK;
}
+18 -17
View File
@@ -37,8 +37,7 @@ s32 cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dialogPara
while (!result)
{
CHECK_EMU_STATUS;
std::this_thread::sleep_for(1ms);
thread_ctrl::wait_for(1000);
}
return CELL_OSKDIALOG_OK;
@@ -131,14 +130,15 @@ s32 cellOskDialogExtInputDeviceUnlock()
return CELL_OSKDIALOG_OK;
}
s32 cellOskDialogExtRegisterKeyboardEventHookCallback()
s32 cellOskDialogExtRegisterKeyboardEventHookCallback(u16 hookEventMode, vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback> pCallback)
{
fmt::throw_exception("Unimplemented" HERE);
cellOskDialog.todo("cellOskDialogExtRegisterKeyboardEventHookCallback(hookEventMode=%u, pCallback=*0x%x)", hookEventMode, pCallback);
return CELL_OSKDIALOG_OK;
}
s32 cellOskDialogExtAddJapaneseOptionDictionary(vm::cptr<char> filePath)
s32 cellOskDialogExtAddJapaneseOptionDictionary(vm::cpptr<char> filePath)
{
cellOskDialog.todo("cellOskDialogExtAddJapaneseOptionDictionary(filePath=*0x%0x)", filePath);
cellOskDialog.todo("cellOskDialogExtAddJapaneseOptionDictionary(filePath=**0x%0x)", filePath);
return CELL_OSKDIALOG_OK;
}
@@ -148,9 +148,9 @@ s32 cellOskDialogExtEnableClipboard()
return CELL_OSKDIALOG_OK;
}
s32 cellOskDialogExtSendFinishMessage(vm::ptr<CellOskDialogFinishReason> finishReason)
s32 cellOskDialogExtSendFinishMessage(s32 /*CellOskDialogFinishReason*/ finishReason)
{
cellOskDialog.todo("cellOskDialogExtSendFinishMessage(finishReason=*0x%x)", finishReason);
cellOskDialog.todo("cellOskDialogExtSendFinishMessage(finishReason=%d)", finishReason);
return CELL_OSKDIALOG_OK;
}
@@ -162,7 +162,7 @@ s32 cellOskDialogExtAddOptionDictionary(vm::cptr<CellOskDialogImeDictionaryInfo>
s32 cellOskDialogExtSetInitialScale(f32 initialScale)
{
cellOskDialog.todo("cellOskDialogExtSetInitialScale(initialScale=0x%x)", initialScale);
cellOskDialog.todo("cellOskDialogExtSetInitialScale(initialScale=%f)", initialScale);
return CELL_OSKDIALOG_OK;
}
@@ -174,24 +174,25 @@ s32 cellOskDialogExtInputDeviceLock()
s32 cellOskDialogExtSetBaseColor(f32 red, f32 blue, f32 green, f32 alpha)
{
cellOskDialog.warning("cellOskDialogExtSetBaseColor(red=0x%x, blue=0x%x, green=0x%x, alpha=0x%x)", red, blue, green, alpha);
cellOskDialog.warning("cellOskDialogExtSetBaseColor(red=%f, blue=%f, green=%f, alpha=%f)", red, blue, green, alpha);
return CELL_OSKDIALOG_OK;
}
s32 cellOskDialogExtRegisterConfirmWordFilterCallback()
s32 cellOskDialogExtRegisterConfirmWordFilterCallback(vm::ptr<cellOskDialogConfirmWordFilterCallback> pCallback)
{
fmt::throw_exception("Unimplemented" HERE);
cellOskDialog.todo("cellOskDialogExtRegisterConfirmWordFilterCallback(pCallback=*0x%x)", pCallback);
return CELL_OSKDIALOG_OK;
}
s32 cellOskDialogExtUpdateInputText()
{
cellOskDialog.todo("cellOskDialogExtUpdateInputText");
cellOskDialog.todo("cellOskDialogExtUpdateInputText()");
return CELL_OSKDIALOG_OK;
}
s32 cellOskDialogExtDisableHalfByteKana()
{
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana");
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana()");
return CELL_OSKDIALOG_OK;
}
@@ -203,7 +204,7 @@ s32 cellOskDialogExtSetPointerEnable(b8 enable)
s32 cellOskDialogExtUpdatePointerDisplayPos()
{
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos()");
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos"); // Missing arguments
return CELL_OSKDIALOG_OK;
}
@@ -213,9 +214,9 @@ s32 cellOskDialogExtEnableHalfByteKana()
return CELL_OSKDIALOG_OK;
}
s32 cellOskDialogExtRegisterForceFinishCallback()
s32 cellOskDialogExtRegisterForceFinishCallback(vm::ptr<cellOskDialogForceFinishCallback> pCallback)
{
cellOskDialog.todo("cellOskDialogExtRegisterForceFinishCallback()");
cellOskDialog.todo("cellOskDialogExtRegisterForceFinishCallback(pCallback=*0x%x)", pCallback);
return CELL_OSKDIALOG_OK;
}
+4
View File
@@ -157,3 +157,7 @@ struct CellOskDialogImeDictionaryInfo
vm::bcptr<char> dictionaryPath;
};
using cellOskDialogConfirmWordFilterCallback = int(vm::ptr<u16> pConfirmString, s32 wordLength);
using cellOskDialogHardwareKeyboardEventHookCallback = class b8(
vm::ptr<CellOskDialogKeyMessage> keyMessage, vm::ptr<u32> action, vm::ptr<void> pActionInfo);
using cellOskDialogForceFinishCallback = class b8();
+2
View File
@@ -1,6 +1,8 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
namespace vm { using namespace ps3; }
logs::channel cellOvis("cellOvis", logs::level::notice);
// Return Codes
+1 -4
View File
@@ -12,15 +12,12 @@ s32 cellPadInit(u32 max_connect)
{
sys_io.warning("cellPadInit(max_connect=%d)", max_connect);
if (max_connect > CELL_PAD_MAX_PORT_NUM)
return CELL_PAD_ERROR_INVALID_PARAMETER;
const auto handler = fxm::import<PadHandlerBase>(Emu.GetCallbacks().get_pad_handler);
if (!handler)
return CELL_PAD_ERROR_ALREADY_INITIALIZED;
handler->Init(max_connect);
handler->Init(std::min(max_connect, CELL_PAD_MAX_PORT_NUM));
return CELL_OK;
}
+357 -221
View File
@@ -65,7 +65,7 @@ void pngDecReadBuffer(png_structp png_ptr, png_bytep out, png_size_t length)
if (buffer.file)
{
// Get the file
auto file = idm::get<lv2_file>(buffer.fd);
auto file = idm::get<lv2_fs_object, lv2_file>(buffer.fd);
// Read the data
file->file.read(out, length);
@@ -83,10 +83,75 @@ void pngDecReadBuffer(png_structp png_ptr, png_bytep out, png_size_t length)
}
}
void pngDecRowCallback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass)
{
PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr);
if (!stream)
{
cellPngDec.error("Failed to obtain streamPtr in rowCallback.");
return;
}
// we have to check this everytime as this func can be called multiple times per row, and/or only once per row
if (stream->nextRow + stream->outputCounts == row_num)
stream->nextRow = row_num;
if (stream->ppuContext && (stream->nextRow == row_num || pass > 0))
{
if (pass > 0 )
{
stream->cbDispInfo->scanPassCount = pass;
stream->cbDispInfo->nextOutputStartY = row_num;
}
else {
stream->cbDispInfo->scanPassCount = 0;
stream->cbDispInfo->nextOutputStartY = 0;
}
stream->cbDispInfo->outputImage = stream->cbDispParam->nextOutputImage;
stream->cbCtrlDisp.cbCtrlDispFunc(*stream->ppuContext, stream->cbDispInfo, stream->cbDispParam, stream->cbCtrlDisp.cbCtrlDispArg);
stream->cbDispInfo->outputStartY = row_num;
}
u8* data;
if (pass > 0)
data = static_cast<u8*>(stream->cbDispParam->nextOutputImage.get_ptr());
else
data = static_cast<u8*>(stream->cbDispParam->nextOutputImage.get_ptr()) + ((row_num - stream->cbDispInfo->outputStartY) * stream->cbDispInfo->outputFrameWidthByte);
png_progressive_combine_row(png_ptr, data, new_row);
}
void pngDecInfoCallback(png_structp png_ptr, png_infop info)
{
PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr);
if (!stream)
{
cellPngDec.error("Failed to obtain streamPtr in rowCallback.");
return;
}
const size_t remaining = png_process_data_pause(png_ptr, false);
stream->buffer->cursor += (stream->buffer->length - remaining);
}
void pngDecEndCallback(png_structp png_ptr, png_infop info)
{
PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr);
if (!stream)
{
cellPngDec.error("Failed to obtain streamPtr in endCallback.");
return;
}
stream->endOfFile = true;
}
// Custom error handler for libpng
void pngDecError(png_structp png_ptr, png_const_charp error_message)
{
cellPngDec.error(error_message);
// we can't return here or libpng blows up
throw LibPngCustomException("Fatal Error in libpng");
}
// Custom warning handler for libpng
@@ -114,7 +179,7 @@ void pngDecWarning(png_structp png_ptr, png_const_charp error_message)
// 14 - sCAL
// 15 - IDAT
// 16:30 - reserved
be_t<u32> pngDecGetChunkInformation(PStream stream, bool IDAT = false)
be_t<u32> pngDecGetChunkInformation(PngStream* stream, bool IDAT = false)
{
// The end result of the chunk information (bigger-endian)
be_t<u32> chunk_information = 0;
@@ -263,9 +328,9 @@ s32 pngDecCreate(ppu_thread& ppu, PPHandle png_handle, PThreadInParam thread_in_
}
// Set the allocation functions in the handle
handle->malloc = thread_in_param->cbCtrlMallocFunc;
handle->malloc_ = thread_in_param->cbCtrlMallocFunc;
handle->malloc_arg = thread_in_param->cbCtrlMallocArg;
handle->free = thread_in_param->cbCtrlFreeFunc;
handle->free_ = thread_in_param->cbCtrlFreeFunc;
handle->free_arg = thread_in_param->cbCtrlFreeArg;
// Set handle pointer
@@ -280,7 +345,7 @@ s32 pngDecCreate(ppu_thread& ppu, PPHandle png_handle, PThreadInParam thread_in_
s32 pngDecDestroy(ppu_thread& ppu, PHandle handle)
{
// Deallocate the decoder handle memory
if (handle->free(ppu, handle, handle->free_arg) != 0)
if (handle->free_(ppu, handle, handle->free_arg) != 0)
{
cellPngDec.error("PNG decoder deallocation failed.");
return CELL_PNGDEC_ERROR_FATAL;
@@ -291,14 +356,15 @@ s32 pngDecDestroy(ppu_thread& ppu, PHandle handle)
s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source, POpenInfo open_info, PCbControlStream control_stream = vm::null, POpenParam open_param = vm::null)
{
// Check if partial image decoding is used
if (control_stream || open_param)
// partial decoding only supported with buffer type
if (source->srcSelect != CELL_PNGDEC_BUFFER && control_stream)
{
fmt::throw_exception("Partial image decoding is not supported." HERE);
cellPngDec.error("Attempted partial image decode with file.");
return CELL_PNGDEC_ERROR_STREAM_FORMAT;
}
// Allocate memory for the stream structure
auto stream = vm::ptr<PngStream>::make(handle->malloc(ppu, sizeof(PngStream), handle->malloc_arg).addr());
auto stream = vm::ptr<PngStream>::make(handle->malloc_(ppu, sizeof(PngStream), handle->malloc_arg).addr());
// Check if the allocation of memory for the stream structure failed
if (!stream)
@@ -313,14 +379,11 @@ s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source
// Set the stream source to the source give by the game
stream->source = *source;
// Indicate that a fixed alpha value isn't used, if not specified otherwise
stream->fixed_alpha = false;
// Use virtual memory address as a handle
*png_stream = stream;
// Allocate memory for the PNG buffer for decoding
auto buffer = vm::ptr<PngBuffer>::make(handle->malloc(ppu, sizeof(PngBuffer), handle->malloc_arg).addr());
auto buffer = vm::ptr<PngBuffer>::make(handle->malloc_(ppu, sizeof(PngBuffer), handle->malloc_arg).addr());
// Check for if the buffer structure allocation failed
if (!buffer)
@@ -364,7 +427,7 @@ s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source
}
// Get the file descriptor
buffer->fd = idm::make<lv2_file>(stream->source.fileName.get_ptr(), std::move(file_stream), 0, 0);
buffer->fd = idm::make<lv2_fs_object, lv2_file>(stream->source.fileName.get_ptr(), std::move(file_stream), 0, 0);
// Indicate that we need to read from a file stream
buffer->file = true;
@@ -401,43 +464,44 @@ s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source
fmt::throw_exception("Creation of png_infop failed." HERE);
}
// Set a point to return to when an error occurs in libpng
if (setjmp(png_jmpbuf(stream->png_ptr)))
{
fmt::throw_exception("Fatal error in libpng." HERE);
}
// We must indicate, that we allocated more memory
open_info->initSpaceAllocated += sizeof(PngBuffer);
// Init the IO for either reading from a file or a buffer
if (source->srcSelect == CELL_PNGDEC_BUFFER)
{
// Set the data pointer and the file size
buffer->length = stream->source.fileSize;
buffer->length = stream->source.streamSize;
buffer->data = stream->source.streamPtr;
// Since we already read the header, we start reading from position 8
buffer->cursor = 8;
}
// Set the custom read function for decoding
png_set_read_fn(stream->png_ptr, buffer.get_ptr(), pngDecReadBuffer);
if (control_stream)
{
if (open_param && open_param->selectChunk != 0)
fmt::throw_exception("Partial Decoding with selectChunk not supported yet.");
// We need to tell libpng, that we already read 8 bytes
png_set_sig_bytes(stream->png_ptr, 8);
stream->cbCtrlStream.cbCtrlStrmArg = control_stream->cbCtrlStrmArg;
stream->cbCtrlStream.cbCtrlStrmFunc = control_stream->cbCtrlStrmFunc;
// Read the basic info of the PNG file
png_read_info(stream->png_ptr, stream->info_ptr);
png_set_progressive_read_fn(stream->png_ptr, (void *)stream.get_ptr(), pngDecInfoCallback, pngDecRowCallback, pngDecEndCallback);
// Read the header info for future use
stream->info.imageWidth = png_get_image_width(stream->png_ptr, stream->info_ptr);
stream->info.imageHeight = png_get_image_height(stream->png_ptr, stream->info_ptr);
stream->info.numComponents = png_get_channels(stream->png_ptr, stream->info_ptr);
stream->info.colorSpace = getPngDecColourType(png_get_color_type(stream->png_ptr, stream->info_ptr));
stream->info.bitDepth = png_get_bit_depth(stream->png_ptr, stream->info_ptr);
stream->info.interlaceMethod = png_get_interlace_type(stream->png_ptr, stream->info_ptr);
stream->info.chunkInformation = pngDecGetChunkInformation(stream);
// push header tag to libpng to keep us in sync
try
{
png_process_data(stream->png_ptr, stream->info_ptr, header, 8);
}
catch (LibPngCustomException&)
{
return CELL_PNGDEC_ERROR_HEADER;
}
}
else
{
png_set_read_fn(stream->png_ptr, buffer.get_ptr(), pngDecReadBuffer);
// We need to tell libpng, that we already read 8 bytes
png_set_sig_bytes(stream->png_ptr, 8);
}
return CELL_OK;
}
@@ -447,13 +511,13 @@ s32 pngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
// Remove the file descriptor, if a file descriptor was used for decoding
if (stream->buffer->file)
{
idm::remove<lv2_file>(stream->buffer->fd);
idm::remove<lv2_fs_object, lv2_file>(stream->buffer->fd);
}
// Deallocate the PNG buffer structure used to decode from memory, if we decoded from memory
if (stream->buffer)
{
if (handle->free(ppu, stream->buffer, handle->free_arg) != 0)
if (handle->free_(ppu, stream->buffer, handle->free_arg) != 0)
{
cellPngDec.error("PNG buffer decoding structure deallocation failed.");
return CELL_PNGDEC_ERROR_FATAL;
@@ -464,7 +528,7 @@ s32 pngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
png_destroy_read_struct(&stream->png_ptr, &stream->info_ptr, nullptr);
// Deallocate the stream memory
if (handle->free(ppu, stream, handle->free_arg) != 0)
if (handle->free_(ppu, stream, handle->free_arg) != 0)
{
cellPngDec.error("PNG stream deallocation failed.");
return CELL_PNGDEC_ERROR_FATAL;
@@ -473,83 +537,139 @@ s32 pngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
return CELL_OK;
}
s32 pngReadHeader(PStream stream, PInfo info, PExtInfo extra_info = vm::null)
void pngSetHeader(PngStream* stream)
{
// Set the pointer to stream info - we already get the header info, when opening the decoder
*info = stream->info;
// Set the reserved value to 0, if passed to the function.
if (extra_info)
{
extra_info->reserved = 0;
}
return CELL_OK;
stream->info.imageWidth = png_get_image_width(stream->png_ptr, stream->info_ptr);
stream->info.imageHeight = png_get_image_height(stream->png_ptr, stream->info_ptr);
stream->info.numComponents = png_get_channels(stream->png_ptr, stream->info_ptr);
stream->info.colorSpace = getPngDecColourType(png_get_color_type(stream->png_ptr, stream->info_ptr));
stream->info.bitDepth = png_get_bit_depth(stream->png_ptr, stream->info_ptr);
stream->info.interlaceMethod = png_get_interlace_type(stream->png_ptr, stream->info_ptr);
stream->info.chunkInformation = pngDecGetChunkInformation(stream);
}
s32 pngDecSetParameter(PStream stream, PInParam in_param, POutParam out_param, PExtInParam extra_in_param = vm::null, PExtOutParam extra_out_param = vm::null)
{
// Partial image decoding is not supported. Need to find games to test with.
if (extra_in_param || extra_out_param)
{
fmt::throw_exception("Partial image decoding is not supported" HERE);
}
if (in_param->outputPackFlag == CELL_PNGDEC_1BYTE_PER_NPIXEL)
{
fmt::throw_exception("Packing not supported! (%d)" HERE, in_param->outputPackFlag);
}
// We already grab the basic info, when opening the stream, so we simply need to pass most of the values
// flag to keep unknown chunks
png_set_keep_unknown_chunks(stream->png_ptr, PNG_HANDLE_CHUNK_IF_SAFE, 0, 0);
// Scale 16 bit depth down to 8 bit depth.
if (stream->info.bitDepth == 16 && in_param->outputBitDepth == 8)
{
// PS3 uses png_set_strip_16, since png_set_scale_16 wasn't available back then.
png_set_strip_16(stream->png_ptr);
}
// This shouldnt ever happen, but not sure what to do if it does, just want it logged for now
if (stream->info.bitDepth != 16 && in_param->outputBitDepth == 16)
cellPngDec.error("Output depth of 16 with non input depth of 16 specified!");
if (in_param->commandPtr != vm::null)
cellPngDec.warning("Ignoring CommandPtr.");
if (stream->info.colorSpace != in_param->outputColorSpace)
{
// check if we need to set alpha
const bool inputHasAlpha = cellPngColorSpaceHasAlpha(stream->info.colorSpace);
const bool outputWantsAlpha = cellPngColorSpaceHasAlpha(in_param->outputColorSpace);
if (outputWantsAlpha && !inputHasAlpha)
{
if (in_param->outputAlphaSelect == CELL_PNGDEC_FIX_ALPHA)
png_set_add_alpha(stream->png_ptr, in_param->outputColorAlpha, in_param->outputColorSpace == CELL_PNGDEC_ARGB ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
else
{
// Check if we can steal the alpha from a trns block
if (png_get_valid(stream->png_ptr, stream->info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(stream->png_ptr);
// if not, just set default of 0xff
else
png_set_add_alpha(stream->png_ptr, 0xff, in_param->outputColorSpace == CELL_PNGDEC_ARGB ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
}
}
else if (inputHasAlpha && !outputWantsAlpha)
png_set_strip_alpha(stream->png_ptr);
else if (in_param->outputColorSpace == CELL_PNGDEC_ARGB && stream->info.colorSpace == CELL_PNGDEC_RGBA)
png_set_swap_alpha(stream->png_ptr);
// Handle gray<->rgb colorspace conversions
// rgb output
if (in_param->outputColorSpace == CELL_PNGDEC_ARGB
|| in_param->outputColorSpace == CELL_PNGDEC_RGBA
|| in_param->outputColorSpace == CELL_PNGDEC_RGB)
{
if (stream->info.colorSpace == CELL_PNGDEC_PALETTE)
png_set_palette_to_rgb(stream->png_ptr);
if ((stream->info.colorSpace == CELL_PNGDEC_GRAYSCALE || stream->info.colorSpace == CELL_PNGDEC_GRAYSCALE_ALPHA)
&& stream->info.bitDepth < 8)
png_set_expand_gray_1_2_4_to_8(stream->png_ptr);
}
// grayscale output
else
{
if (stream->info.colorSpace == CELL_PNGDEC_ARGB
|| stream->info.colorSpace == CELL_PNGDEC_RGBA
|| stream->info.colorSpace == CELL_PNGDEC_RGB)
{
png_set_rgb_to_gray(stream->png_ptr, PNG_ERROR_ACTION_NONE, PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
}
else {
// not sure what to do here
cellPngDec.error("Grayscale / Palette to Grayscale / Palette conversion currently unsupported.");
}
}
}
stream->passes = png_set_interlace_handling(stream->png_ptr);
// Update the info structure
png_read_update_info(stream->png_ptr, stream->info_ptr);
stream->out_param.outputWidth = stream->info.imageWidth;
stream->out_param.outputHeight = stream->info.imageHeight;
stream->out_param.outputBitDepth = in_param->outputBitDepth;
stream->out_param.outputColorSpace = in_param->outputColorSpace;
stream->out_param.outputBitDepth = stream->info.bitDepth;
stream->out_param.outputMode = in_param->outputMode;
stream->out_param.outputWidthByte = png_get_rowbytes(stream->png_ptr, stream->info_ptr);
stream->out_param.outputComponents = png_get_channels(stream->png_ptr, stream->info_ptr);
stream->packing = in_param->outputPackFlag;
// Check if a fixed alpha value is specified
if (in_param->outputAlphaSelect == CELL_PNGDEC_FIX_ALPHA)
{
// We set the fixed alpha value in the stream structure, for padding while decoding
stream->fixed_alpha = true;
stream->fixed_alpha_colour = in_param->outputColorAlpha;
}
// Remap the number of output components based on the passed colorSpace value
switch (stream->out_param.outputColorSpace)
{
case CELL_PNGDEC_RGBA:
case CELL_PNGDEC_ARGB:
{
stream->out_param.outputComponents = 4;
break;
}
case CELL_PNGDEC_RGB:
{
stream->out_param.outputComponents = 3;
break;
}
case CELL_PNGDEC_GRAYSCALE_ALPHA:
{
stream->out_param.outputComponents = 2;
break;
}
case CELL_PNGDEC_PALETTE:
case CELL_PNGDEC_GRAYSCALE:
{
stream->out_param.outputComponents = 1;
}
}
// Set the memory usage. We currently don't actually allocate memory for libpng through the callbacks, due to libpng needing a lot more memory compared to PS3 variant.
stream->out_param.useMemorySpace = 0;
// Set the pointer
if (extra_in_param)
{
if (extra_in_param->bufferMode != CELL_PNGDEC_LINE_MODE)
{
cellPngDec.error("Invalid Buffermode specified.");
return CELL_PNGDEC_ERROR_ARG;
}
if (stream->passes > 1)
{
stream->outputCounts = 1;
}
else
stream->outputCounts = extra_in_param->outputCounts;
if (extra_out_param)
{
if (stream->outputCounts == 0)
extra_out_param->outputHeight = stream->out_param.outputHeight;
else
extra_out_param->outputHeight = std::min(stream->outputCounts, stream->out_param.outputHeight.value());
extra_out_param->outputWidthByte = stream->out_param.outputWidthByte;
}
}
*out_param = stream->out_param;
return CELL_OK;
@@ -557,145 +677,123 @@ s32 pngDecSetParameter(PStream stream, PInParam in_param, POutParam out_param, P
s32 pngDecodeData(ppu_thread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null)
{
if (cb_control_disp || disp_param)
{
fmt::throw_exception("Partial image decoding is not supported" HERE);
}
// Indicate, that the PNG decoding is stopped/failed. This is incase, we return an error code in the middle of decoding
data_out_info->status = CELL_PNGDEC_DEC_STATUS_STOP;
// Possibilities for decoding in different sizes aren't tested, so if anyone finds any of these cases, we'll know about it.
if (stream->info.imageWidth != stream->out_param.outputWidth)
{
fmt::throw_exception("Image width doesn't match output width! (%d/%d)" HERE, stream->out_param.outputWidth, stream->info.imageWidth);
}
if (stream->info.imageHeight != stream->out_param.outputHeight)
{
fmt::throw_exception("Image width doesn't match output height! (%d/%d)" HERE, stream->out_param.outputHeight, stream->info.imageHeight);
}
// Get the amount of output bytes per line
const u32 bytes_per_line = data_control_param->outputBytesPerLine;
// Whether to recaculate bytes per row
bool recalculate_bytes_per_row = false;
// Check if the game is expecting the number of bytes per line to be lower, than the actual bytes per line on the image. (Arkedo Pixel for example)
// In such case we strip the bit depth to be lower.
if ((bytes_per_line < stream->out_param.outputWidthByte) && stream->out_param.outputBitDepth != 8)
// Log this for now
if (bytes_per_line < stream->out_param.outputWidthByte)
{
// Check if the packing is really 1 byte per 1 pixel
if (stream->packing != CELL_PNGDEC_1BYTE_PER_1PIXEL)
fmt::throw_exception("Bytes per line less than expected output! Got: %d, expected: %d" HERE, bytes_per_line, stream->out_param.outputWidthByte);
}
// partial decoding
if (cb_control_disp && stream->outputCounts > 0)
{
// get data from cb
auto streamInfo = vm::ptr<CellPngDecStrmInfo>::make(handle->malloc_(ppu, sizeof(CellPngDecStrmInfo), handle->malloc_arg).addr());
auto streamParam = vm::ptr<CellPngDecStrmParam>::make(handle->malloc_(ppu, sizeof(CellPngDecStrmParam), handle->malloc_arg).addr());
stream->cbDispInfo = vm::ptr<CellPngDecDispInfo>::make(handle->malloc_(ppu, sizeof(CellPngDecDispInfo), handle->malloc_arg).addr());
stream->cbDispParam = vm::ptr<CellPngDecDispParam>::make(handle->malloc_(ppu, sizeof(CellPngDecDispParam), handle->malloc_arg).addr());
auto freeMem = [&]()
{
fmt::throw_exception("Unexpected packing value! (%d)" HERE, stream->packing);
}
handle->free_(ppu, streamInfo, handle->free_arg);
handle->free_(ppu, streamParam, handle->free_arg);
handle->free_(ppu, stream->cbDispInfo, handle->free_arg);
handle->free_(ppu, stream->cbDispParam, handle->free_arg);
};
// Scale 16 bit depth down to 8 bit depth. PS3 uses png_set_strip_16, since png_set_scale_16 wasn't available back then.
png_set_strip_16(stream->png_ptr);
recalculate_bytes_per_row = true;
}
// Check if the outputWidthByte is smaller than the intended output length of a line. For example an image might be in RGB, but we need to output 4 components, so we need to perform alpha padding.
else if (stream->out_param.outputWidthByte < (stream->out_param.outputWidth * stream->out_param.outputComponents))
{
// If fixed alpha is not specified in such a case, the default value for the alpha is 0xFF (255)
if (!stream->fixed_alpha)
// set things that won't change between callbacks
stream->cbDispInfo->outputFrameWidthByte = bytes_per_line;
stream->cbDispInfo->outputFrameHeight = stream->out_param.outputHeight;
stream->cbDispInfo->outputWidthByte = stream->out_param.outputWidthByte;
stream->cbDispInfo->outputBitDepth = stream->out_param.outputBitDepth;
stream->cbDispInfo->outputComponents = stream->out_param.outputComponents;
stream->cbDispInfo->outputHeight = stream->outputCounts;
stream->cbDispInfo->outputStartXByte = 0;
stream->cbDispInfo->outputStartY = 0;
stream->cbDispInfo->scanPassCount = 0;
stream->cbDispInfo->nextOutputStartY = 0;
stream->ppuContext = &ppu;
stream->nextRow = stream->cbDispInfo->outputHeight;
stream->cbCtrlDisp.cbCtrlDispArg = cb_control_disp->cbCtrlDispArg;
stream->cbCtrlDisp.cbCtrlDispFunc = cb_control_disp->cbCtrlDispFunc;
stream->cbDispParam->nextOutputImage = disp_param->nextOutputImage;
streamInfo->decodedStrmSize = stream->buffer->cursor;
// push the rest of the buffer we have
if (stream->buffer->length > stream->buffer->cursor)
{
stream->fixed_alpha_colour = 0xFF;
}
// We need to fill alpha (before or after, depending on the output colour format) using the fixed alpha value passed by the game.
png_set_add_alpha(stream->png_ptr, stream->fixed_alpha_colour, stream->out_param.outputColorSpace == CELL_PNGDEC_RGBA ? PNG_FILLER_AFTER : PNG_FILLER_BEFORE);
recalculate_bytes_per_row = true;
}
// We decode as RGBA, so we need to swap the alpha
else if (stream->out_param.outputColorSpace == CELL_PNGDEC_ARGB)
{
// Swap the alpha channel for the ARGB output format, if the padding isn't needed
png_set_swap_alpha(stream->png_ptr);
}
// Sometimes games pass in a RBG/RGBA image and want it as grayscale
else if ((stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE_ALPHA || stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE)
&& (stream->info.colorSpace == CELL_PNGDEC_RGB || stream->info.colorSpace == CELL_PNGDEC_RGBA))
{
// Tell libpng to convert it to grayscale
png_set_rgb_to_gray(stream->png_ptr, PNG_ERROR_ACTION_NONE, PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
recalculate_bytes_per_row = true;
}
if (recalculate_bytes_per_row)
{
// Update the info structure
png_read_update_info(stream->png_ptr, stream->info_ptr);
// Recalculate the bytes per row
stream->out_param.outputWidthByte = png_get_rowbytes(stream->png_ptr, stream->info_ptr);
}
// Calculate the image size
u32 image_size = stream->out_param.outputWidthByte * stream->out_param.outputHeight;
// Buffer for storing the image
std::vector<u8> png(image_size);
// Make an unique pointer for the row pointers
std::vector<u8*> row_pointers(stream->out_param.outputHeight);
// Allocate memory for rows
for (u32 y = 0; y < stream->out_param.outputHeight; y++)
{
row_pointers[y] = &png[y * stream->out_param.outputWidthByte];
}
// Decode the image
png_read_image(stream->png_ptr, row_pointers.data());
// Check if the image needs to be flipped
const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP;
// Copy the result to the output buffer
switch (stream->out_param.outputColorSpace)
{
case CELL_PNGDEC_RGB:
case CELL_PNGDEC_RGBA:
case CELL_PNGDEC_ARGB:
case CELL_PNGDEC_GRAYSCALE_ALPHA:
{
// Check if we need to flip the image or need to leave empty bytes at the end of a line
if ((bytes_per_line > stream->out_param.outputWidthByte) || flip)
{
// Get how many bytes per line we need to output - bytesPerLine is total amount of bytes per line, rest is unused and the game can do as it pleases.
const u32 line_size = std::min(bytes_per_line, stream->out_param.outputWidth * 4);
// If the game wants more bytes per line to be output, than the image has, then we simply copy what we have for each line,
// and continue on the next line, thus leaving empty bytes at the end of the line.
for (u32 i = 0; i < stream->out_param.outputHeight; i++)
u8* data = static_cast<u8*>(stream->buffer->data.get_ptr()) + stream->buffer->cursor;
try
{
const u32 dst = i * bytes_per_line;
const u32 src = stream->out_param.outputWidth * 4 * (flip ? stream->out_param.outputHeight - i - 1 : i);
memcpy(&data[dst], &png[src], line_size);
png_process_data(stream->png_ptr, stream->info_ptr, data, stream->buffer->length - stream->buffer->cursor);
}
catch (LibPngCustomException&)
{
freeMem();
return CELL_PNGDEC_ERROR_FATAL;
}
streamInfo->decodedStrmSize = stream->buffer->length;
}
// todo: commandPtr
// then just loop until the end, the callbacks should take care of the rest
while (stream->endOfFile != true)
{
stream->cbCtrlStream.cbCtrlStrmFunc(ppu, streamInfo, streamParam, stream->cbCtrlStream.cbCtrlStrmArg);
streamInfo->decodedStrmSize += streamParam->strmSize;
try
{
png_process_data(stream->png_ptr, stream->info_ptr, static_cast<u8*>(streamParam->strmPtr.get_ptr()), streamParam->strmSize);
}
catch (LibPngCustomException&)
{
freeMem();
return CELL_PNGDEC_ERROR_FATAL;
}
}
else
{
// We can simply copy the output to the data pointer specified by the game, since we already do alpha channel transformations in libpng, if needed
memcpy(data.get_ptr(), png.data(), image_size);
}
break;
}
default: fmt::throw_exception("Unsupported color space (%d)" HERE, stream->out_param.outputColorSpace);
freeMem();
}
else
{
// Check if the image needs to be flipped
const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP;
// Decode the image
// todo: commandptr
try
{
for (int j = 0; j < stream->passes; j++)
{
for (int i = 0; i < stream->out_param.outputHeight; ++i)
{
const u32 line = flip ? stream->out_param.outputHeight - i - 1 : i;
png_read_row(stream->png_ptr, &data[line*bytes_per_line], nullptr);
}
}
png_read_end(stream->png_ptr, stream->info_ptr);
}
catch (LibPngCustomException&)
{
return CELL_PNGDEC_ERROR_FATAL;
}
}
// Get the number of iTXt, tEXt and zTXt chunks
s32 text_chunks = 0;
png_get_text(stream->png_ptr, stream->info_ptr, nullptr, &text_chunks);
const s32 text_chunks = png_get_text(stream->png_ptr, stream->info_ptr, nullptr, nullptr);
// Set the chunk information and the previously obtained number of text chunks
data_out_info->numText = (u32)text_chunks;
data_out_info->chunkInformation = pngDecGetChunkInformation(stream, true);
data_out_info->numUnknownChunk = 0; // TODO: Get this somehow. Does anything even use or need this?
data_out_info->chunkInformation = pngDecGetChunkInformation(stream.get_ptr(), true);
png_unknown_chunkp unknowns;
const int num_unknowns = png_get_unknown_chunks(stream->png_ptr, stream->info_ptr, &unknowns);
data_out_info->numUnknownChunk = num_unknowns;
// Indicate that the decoding succeeded
data_out_info->status = CELL_PNGDEC_DEC_STATUS_FINISH;
@@ -711,7 +809,7 @@ s32 cellPngDecCreate(ppu_thread& ppu, PPHandle handle, PThreadInParam threadInPa
s32 cellPngDecExtCreate(ppu_thread& ppu, PPHandle handle, PThreadInParam threadInParam, PThreadOutParam threadOutParam, PExtThreadInParam extThreadInParam, PExtThreadOutParam extThreadOutParam)
{
cellPngDec.warning("cellPngDecCreate(mainHandle=**0x%x, threadInParam=*0x%x, threadOutParam=*0x%x, extThreadInParam=*0x%x, extThreadOutParam=*0x%x)", handle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
cellPngDec.warning("cellPngDecExtCreate(mainHandle=**0x%x, threadInParam=*0x%x, threadOutParam=*0x%x, extThreadInParam=*0x%x, extThreadOutParam=*0x%x)", handle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
return pngDecCreate(ppu, handle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
}
@@ -742,13 +840,51 @@ s32 cellPngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
s32 cellPngDecReadHeader(PHandle handle, PStream stream, PInfo info)
{
cellPngDec.warning("cellPngDecReadHeader(handle=*0x%x, stream=*0x%x, info=*0x%x)", handle, stream, info);
return pngReadHeader(stream, info);
// Read the header info
png_read_info(stream->png_ptr, stream->info_ptr);
pngSetHeader(stream.get_ptr());
// Set the pointer to stream info
*info = stream->info;
return CELL_OK;
}
s32 cellPngDecExtReadHeader(PHandle handle, PStream stream, PInfo info, PExtInfo extInfo)
{
cellPngDec.warning("cellPngDecExtReadHeader(handle=*0x%x, stream=*0x%x, info=*0x%x, extInfo=*0x%x)", handle, stream, info, extInfo);
return pngReadHeader(stream, info, extInfo);
// Set the reserved value to 0, if passed to the function. (Should this be arg error if they dont pass?)
if (extInfo)
{
extInfo->reserved = 0;
}
// lets push what we have so far
u8* data = static_cast<u8*>(stream->buffer->data.get_ptr()) + stream->buffer->cursor;
try
{
png_process_data(stream->png_ptr, stream->info_ptr, data, stream->buffer->length);
}
catch (LibPngCustomException&)
{
return CELL_PNGDEC_ERROR_HEADER;
}
// lets hope we pushed enough for callback
pngSetHeader(stream.get_ptr());
// png doesnt allow empty image, so quick check for 0 verifys if we got the header
// not sure exactly what should happen if we dont have header, ask for more data with callback?
if (stream->info.imageWidth == 0)
{
fmt::throw_exception("Invalid or not enough data sent to get header");
return CELL_PNGDEC_ERROR_HEADER;
}
// Set the pointer to stream info
*info = stream->info;
return CELL_OK;
}
s32 cellPngDecSetParameter(PHandle handle, PStream stream, PInParam inParam, POutParam outParam)
+35 -9
View File
@@ -1,5 +1,4 @@
#pragma once
namespace vm { using namespace ps3; }
#include "cellPng.h"
@@ -264,9 +263,9 @@ struct CellPngDecCbCtrlDisp
// Custom structs
struct PngHandle
{
vm::ptr<CellPngDecCbControlMalloc> malloc;
vm::ptr<CellPngDecCbControlMalloc> malloc_;
vm::ptr<void> malloc_arg;
vm::ptr<CellPngDecCbControlFree> free;
vm::ptr<CellPngDecCbControlFree> free_;
vm::ptr<void> free_arg;
};
@@ -289,16 +288,22 @@ struct PngStream
CellPngDecInfo info;
CellPngDecOutParam out_param;
CellPngDecSrc source;
CellPngDecStrmInfo streamInfo;
CellPngDecStrmParam streamParam;
// Fixed alpha value and flag
bool fixed_alpha;
be_t<u32> fixed_alpha_colour;
// Partial decoding
CellPngDecCbCtrlStrm cbCtrlStream;
CellPngDecCbCtrlDisp cbCtrlDisp;
vm::ptr<CellPngDecDispInfo> cbDispInfo;
vm::ptr<CellPngDecDispParam> cbDispParam;
ppu_thread* ppuContext;
u32 outputCounts = 0;
u32 nextRow = 0;
bool endOfFile = false;
// Pixel packing value
be_t<s32> packing;
u32 passes;
// PNG custom read function structure, for decoding from a buffer
vm::ptr<PngBuffer> buffer;
@@ -320,3 +325,24 @@ static s32 getPngDecColourType(u8 type)
default: fmt::throw_exception("Unknown colour type: %d" HERE, type);
}
}
static bool cellPngColorSpaceHasAlpha(u32 colorspace)
{
switch (colorspace)
{
case CELL_PNGDEC_RGBA:
case CELL_PNGDEC_ARGB:
case CELL_PNGDEC_GRAYSCALE_ALPHA:
return true;
default:
return false;
}
}
// Custom exception for libPng errors
class LibPngCustomException : public std::runtime_error
{
public:
LibPngCustomException(char const* const message) : runtime_error(message) {}
};
+2
View File
@@ -1032,6 +1032,8 @@ s32 cellSailPlayerUnregisterSource()
DECLARE(ppu_module_manager::cellSail)("cellSail", []()
{
static ppu_static_module cellSailAvi("cellSailAvi");
REG_FUNC(cellSail, cellSailMemAllocatorInitialize);
REG_FUNC(cellSail, cellSailFutureInitialize);
+3
View File
@@ -256,6 +256,9 @@ s32 cellSailRecorderDumpImage()
DECLARE(ppu_module_manager::cellSailRec)("cellSailRec", []()
{
static ppu_static_module cellMp4("cellMp4");
static ppu_static_module cellApostSrcMini("cellApostSrcMini");
REG_FUNC(cellSailRec, cellSailProfileSetEsAudioParameter);
REG_FUNC(cellSailRec, cellSailProfileSetEsVideoParameter);
REG_FUNC(cellSailRec, cellSailProfileSetStreamParameter);
+9
View File
@@ -12,6 +12,10 @@
logs::channel cellSaveData("cellSaveData", logs::level::notice);
SaveDialogBase::~SaveDialogBase()
{
}
// cellSaveData aliases (only for cellSaveData.cpp)
using PSetList = vm::ptr<CellSaveDataSetList>;
using PSetBuf = vm::ptr<CellSaveDataSetBuf>;
@@ -572,6 +576,11 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
case CELL_SAVEDATA_FILEOP_READ:
{
fs::file file(dir_path + file_path, fs::read);
if (!file)
{
cellSaveData.error("savedata file not found");
return CELL_SAVEDATA_ERROR_FAILURE;
}
file.seek(fileSet->fileOffset);
fileGet->excSize = static_cast<u32>(file.read(fileSet->fileBuf.get_ptr(), std::min<u32>(fileSet->fileSize, fileSet->fileBufSize)));
break;
+1 -1
View File
@@ -292,7 +292,7 @@ struct SaveDataEntry
class SaveDialogBase
{
public:
virtual ~SaveDialogBase() = default;
virtual ~SaveDialogBase();
virtual s32 ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, vm::ptr<CellSaveDataListSet> listSet) = 0;
};
+2
View File
@@ -1,6 +1,8 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
namespace vm { using namespace ps3; }
logs::channel cellSpudll("cellSpudll", logs::level::notice);
s32 cellSpudllGetImageSize(vm::ptr<u32> psize, vm::cptr<void> so_elf, vm::cptr<struct CellSpudllHandleConfig> config)
+30 -31
View File
@@ -17,6 +17,8 @@
logs::channel cellSpurs("cellSpurs", logs::level::notice);
s32 sys_spu_image_close(vm::ptr<sys_spu_image_t> img);
// TODO
struct cell_error_t
{
@@ -82,7 +84,7 @@ namespace _spurs
s32 add_default_syswkl(vm::ptr<CellSpurs> spurs, vm::cptr<u8> swlPriority, u32 swlMaxSpu, u32 swlIsPreem);
// Destroy the SPURS SPU threads and thread group
s32 finalize_spu(vm::ptr<CellSpurs> spurs);
s32 finalize_spu(ppu_thread&, vm::ptr<CellSpurs> spurs);
// Stop the event helper thread
s32 stop_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
@@ -274,7 +276,7 @@ namespace _spurs
//s32 cellSpursEventFlagWait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode);
//s32 cellSpursEventFlagTryWait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode);
//s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag);
//s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag);
//s32 cellSpursEventFlagDetachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag);
//s32 cellSpursEventFlagGetDirection(vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u32> direction);
//s32 cellSpursEventFlagGetClearMode(vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u32> clear_mode);
//s32 cellSpursEventFlagGetTasksetAddress(vm::ptr<CellSpursEventFlag> eventFlag, vm::pptr<CellSpursTaskset> taskset);
@@ -378,7 +380,7 @@ s32 _spurs::create_lv2_eq(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<u32
if (s32 rc = _spurs::attach_lv2_eq(ppu, spurs, *queueId, port, 1, true))
{
sys_event_queue_destroy(*queueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
sys_event_queue_destroy(ppu, *queueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
}
return CELL_OK;
@@ -487,8 +489,6 @@ void _spurs::handler_wait_ready(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
while (true)
{
CHECK_EMU_STATUS;
if (spurs->handlerExiting)
{
CHECK_SUCCESS(CALL_FUNC(ppu, sys_lwmutex_unlock, ppu, spurs.ptr(&CellSpurs::mutex)));
@@ -567,16 +567,14 @@ void _spurs::handler_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
while (true)
{
CHECK_EMU_STATUS;
if (spurs->flags1 & SF1_EXIT_IF_NO_WORK)
{
_spurs::handler_wait_ready(ppu, spurs);
}
CHECK_SUCCESS(sys_spu_thread_group_start(spurs->spuTG));
CHECK_SUCCESS(sys_spu_thread_group_start(ppu, spurs->spuTG));
if (s32 rc = sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null))
if (s32 rc = sys_spu_thread_group_join(ppu, spurs->spuTG, vm::null, vm::null))
{
if (rc == CELL_ESTAT)
{
@@ -676,7 +674,7 @@ s32 _spurs::wakeup_shutdown_completion_waiter(ppu_thread& ppu, vm::ptr<CellSpurs
if (!wklF->hook || wklEvent->load() & 0x10)
{
verify(HERE), (wklF->x28 == 2);
rc = sys_semaphore_post((u32)wklF->sem, 1);
rc = sys_semaphore_post(ppu, (u32)wklF->sem, 1);
}
return rc;
@@ -714,11 +712,11 @@ void _spurs::event_helper_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++)
{
sys_semaphore_post((u32)spurs->wklF1[i].sem, 1);
sys_semaphore_post(ppu, (u32)spurs->wklF1[i].sem, 1);
if (spurs->flags1 & SF1_32_WORKLOADS)
{
sys_semaphore_post((u32)spurs->wklF2[i].sem, 1);
sys_semaphore_post(ppu, (u32)spurs->wklF2[i].sem, 1);
}
}
}
@@ -749,7 +747,7 @@ void _spurs::event_helper_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
}
else if (data0 == 2)
{
CHECK_SUCCESS(sys_semaphore_post((u32)spurs->semPrv, 1));
CHECK_SUCCESS(sys_semaphore_post(ppu, (u32)spurs->semPrv, 1));
}
else if (data0 == 3)
{
@@ -777,7 +775,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 p
return CELL_SPURS_CORE_ERROR_AGAIN;
}
sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
return CELL_SPURS_CORE_ERROR_AGAIN;
}
@@ -790,7 +788,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 p
return CELL_SPURS_CORE_ERROR_STAT;
}
sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
return CELL_SPURS_CORE_ERROR_STAT;
}
@@ -816,7 +814,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 p
return CELL_SPURS_CORE_ERROR_STAT;
}
sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
return CELL_SPURS_CORE_ERROR_STAT;
}
@@ -841,13 +839,13 @@ s32 _spurs::add_default_syswkl(vm::ptr<CellSpurs> spurs, vm::cptr<u8> swlPriorit
return CELL_OK;
}
s32 _spurs::finalize_spu(vm::ptr<CellSpurs> spurs)
s32 _spurs::finalize_spu(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
if (spurs->flags & SAF_UNKNOWN_FLAG_7 || spurs->flags & SAF_UNKNOWN_FLAG_8)
{
while (true)
{
CHECK_SUCCESS(sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null));
CHECK_SUCCESS(sys_spu_thread_group_join(ppu, spurs->spuTG, vm::null, vm::null));
if (s32 rc = sys_spu_thread_group_destroy(spurs->spuTG))
{
@@ -882,7 +880,7 @@ s32 _spurs::stop_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
return CELL_SPURS_CORE_ERROR_STAT;
}
if (sys_event_port_send(spurs->eventPort, 0, 1, 0) != CELL_OK)
if (sys_event_port_send(ppu, spurs->eventPort, 0, 1, 0) != CELL_OK)
{
return CELL_SPURS_CORE_ERROR_STAT;
}
@@ -897,7 +895,7 @@ s32 _spurs::stop_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
CHECK_SUCCESS(sys_event_port_disconnect(spurs->eventPort));
CHECK_SUCCESS(sys_event_port_destroy(spurs->eventPort));
CHECK_SUCCESS(_spurs::detach_lv2_eq(spurs, spurs->spuPort, true));
CHECK_SUCCESS(sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE));
CHECK_SUCCESS(sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE));
return CELL_OK;
}
@@ -1120,9 +1118,10 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
}
// entry point cannot be initialized immediately because SPU LS will be rewritten by sys_spu_thread_group_start()
idm::get<SPUThread>(spurs->spus[num])->custom_task = [entry = spurs->spuImg.entry_point](SPUThread& spu)
//idm::get<SPUThread>(spurs->spus[num])->custom_task = [entry = spurs->spuImg.entry_point](SPUThread& spu)
{
spu.RegisterHleFunction(entry, spursKernelEntry);
// Disabled
//spu.RegisterHleFunction(entry, spursKernelEntry);
};
}
@@ -1143,7 +1142,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
// Create a mutex to protect access to SPURS handler thread data
if (s32 rc = sys_lwmutex_create(lwMutex, vm::make_var(sys_lwmutex_attribute_t{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, "_spuPrv" })))
{
_spurs::finalize_spu(spurs);
_spurs::finalize_spu(ppu, spurs);
return rollback(), rc;
}
@@ -1151,7 +1150,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
if (s32 rc = sys_lwcond_create(lwCond, lwMutex, vm::make_var(sys_lwcond_attribute_t{ "_spuPrv" })))
{
sys_lwmutex_destroy(ppu, lwMutex);
_spurs::finalize_spu(spurs);
_spurs::finalize_spu(ppu, spurs);
return rollback(), rc;
}
@@ -1168,7 +1167,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
{
sys_lwcond_destroy(lwCond);
sys_lwmutex_destroy(ppu, lwMutex);
_spurs::finalize_spu(spurs);
_spurs::finalize_spu(ppu, spurs);
return rollback(), rc;
}
@@ -1178,7 +1177,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
_spurs::stop_event_helper(ppu, spurs);
sys_lwcond_destroy(lwCond);
sys_lwmutex_destroy(ppu, lwMutex);
_spurs::finalize_spu(spurs);
_spurs::finalize_spu(ppu, spurs);
return rollback(), rc;
}
@@ -1190,7 +1189,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
_spurs::stop_event_helper(ppu, spurs);
sys_lwcond_destroy(lwCond);
sys_lwmutex_destroy(ppu, lwMutex);
_spurs::finalize_spu(spurs);
_spurs::finalize_spu(ppu, spurs);
return rollback(), rc;
}
@@ -2789,7 +2788,7 @@ s32 cellSpursEventFlagSet(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag
// Signal the PPU thread to be woken up
eventFlag->pendingRecvTaskEvents[ppuWaitSlot] = ppuEvents;
CHECK_SUCCESS(sys_event_port_send(eventFlag->eventPortId, 0, 0, 0));
CHECK_SUCCESS(sys_event_port_send(ppu, eventFlag->eventPortId, 0, 0, 0));
}
if (pendingRecv)
@@ -3069,7 +3068,7 @@ s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEven
if (_spurs::detach_lv2_eq(spurs, *port, true) == CELL_OK)
{
sys_event_queue_destroy(*eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
sys_event_queue_destroy(ppu, *eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
}
return failure(rc);
@@ -3079,7 +3078,7 @@ s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEven
}
/// Detach an LV2 event queue from SPURS event flag
s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
s32 cellSpursEventFlagDetachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag)
{
cellSpurs.warning("cellSpursEventFlagDetachLv2EventQueue(eventFlag=*0x%x)", eventFlag);
@@ -3133,7 +3132,7 @@ s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
if (rc == CELL_OK)
{
rc = sys_event_queue_destroy(eventFlag->eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
rc = sys_event_queue_destroy(ppu, eventFlag->eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
}
return CELL_OK;
+48 -57
View File
@@ -10,6 +10,7 @@
#include "cellSpurs.h"
#include <thread>
#include <mutex>
//----------------------------------------------------------------------------
// Externs
@@ -265,10 +266,10 @@ bool spursKernel1SelectWorkload(SPUThread& spu)
u32 wklSelectedId;
u32 pollStatus;
vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
//vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
{
// lock the first 0x80 bytes of spurs
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
// Calculate the contention (number of SPUs used) for each workload
u8 contention[CELL_SPURS_MAX_WORKLOAD];
@@ -428,7 +429,7 @@ bool spursKernel1SelectWorkload(SPUThread& spu)
}
std::memcpy(vm::base(spu.offset + 0x100), spurs, 128);
});
}//);
u64 result = (u64)wklSelectedId << 32;
result |= pollStatus;
@@ -449,10 +450,10 @@ bool spursKernel2SelectWorkload(SPUThread& spu)
u32 wklSelectedId;
u32 pollStatus;
vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
//vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
{
// lock the first 0x80 bytes of spurs
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
// Calculate the contention (number of SPUs used) for each workload
u8 contention[CELL_SPURS_MAX_WORKLOAD2];
@@ -601,7 +602,7 @@ bool spursKernel2SelectWorkload(SPUThread& spu)
}
std::memcpy(vm::base(spu.offset + 0x100), spurs, 128);
});
}//);
u64 result = (u64)wklSelectedId << 32;
result |= pollStatus;
@@ -632,10 +633,10 @@ void spursKernelDispatchWorkload(SPUThread& spu, u64 widAndPollStatus)
switch (wklInfo->addr.addr())
{
case SPURS_IMG_ADDR_SYS_SRV_WORKLOAD:
spu.RegisterHleFunction(0xA00, spursSysServiceEntry);
//spu.RegisterHleFunction(0xA00, spursSysServiceEntry);
break;
case SPURS_IMG_ADDR_TASKSET_PM:
spu.RegisterHleFunction(0xA00, spursTasksetEntry);
//spu.RegisterHleFunction(0xA00, spursTasksetEntry);
break;
default:
std::memcpy(vm::base(spu.offset + 0xA00), wklInfo->addr.get_ptr(), wklInfo->size);
@@ -685,11 +686,7 @@ bool spursKernelWorkloadExit(SPUThread& spu)
// SPURS kernel entry point
bool spursKernelEntry(SPUThread& spu)
{
while (true)
{
std::this_thread::sleep_for(100ms);
CHECK_EMU_STATUS;
}
thread_ctrl::eternalize();
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
memset(ctxt, 0, sizeof(SpursKernelContext));
@@ -724,10 +721,10 @@ bool spursKernelEntry(SPUThread& spu)
}
// Register SPURS kernel HLE functions
spu.UnregisterHleFunctions(0, 0x40000/*LS_BOTTOM*/);
spu.RegisterHleFunction(isKernel2 ? CELL_SPURS_KERNEL2_ENTRY_ADDR : CELL_SPURS_KERNEL1_ENTRY_ADDR, spursKernelEntry);
spu.RegisterHleFunction(ctxt->exitToKernelAddr, spursKernelWorkloadExit);
spu.RegisterHleFunction(ctxt->selectWorkloadAddr, isKernel2 ? spursKernel2SelectWorkload : spursKernel1SelectWorkload);
//spu.UnregisterHleFunctions(0, 0x40000/*LS_BOTTOM*/);
//spu.RegisterHleFunction(isKernel2 ? CELL_SPURS_KERNEL2_ENTRY_ADDR : CELL_SPURS_KERNEL1_ENTRY_ADDR, spursKernelEntry);
//spu.RegisterHleFunction(ctxt->exitToKernelAddr, spursKernelWorkloadExit);
//spu.RegisterHleFunction(ctxt->selectWorkloadAddr, isKernel2 ? spursKernel2SelectWorkload : spursKernel1SelectWorkload);
// Start the system service
spursKernelDispatchWorkload(spu, ((u64)CELL_SPURS_SYS_SERVICE_WORKLOAD_ID) << 32);
@@ -772,11 +769,9 @@ void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt)
{
bool shouldExit;
std::unique_lock<named_thread> lock(spu, std::defer_lock);
while (true)
{
vm::reservation_acquire(vm::base(spu.offset + 0x100), vm::cast(ctxt->spurs.addr(), HERE), 128);
//vm::reservation_acquire(vm::base(spu.offset + 0x100), vm::cast(ctxt->spurs.addr(), HERE), 128);
auto spurs = vm::_ptr<CellSpurs>(spu.offset + 0x100);
// Find the number of SPUs that are idling in this SPURS instance
@@ -861,13 +856,11 @@ void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt)
if (spuIdling && shouldExit == false && foundReadyWorkload == false)
{
// The system service blocks by making a reservation and waiting on the lock line reservation lost event.
CHECK_EMU_STATUS;
if (!lock) { lock.lock(); continue; }
thread_ctrl::wait_for(1000);
continue;
}
if (vm::reservation_update(vm::cast(ctxt->spurs.addr(), HERE), vm::base(spu.offset + 0x100), 128) && (shouldExit || foundReadyWorkload))
//if (vm::reservation_update(vm::cast(ctxt->spurs.addr(), HERE), vm::base(spu.offset + 0x100), 128) && (shouldExit || foundReadyWorkload))
{
break;
}
@@ -895,11 +888,11 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
{
ctxt->sysSrvInitialised = 1;
vm::reservation_acquire(vm::base(spu.offset + 0x100), vm::cast(ctxt->spurs.addr(), HERE), 128);
//vm::reservation_acquire(vm::base(spu.offset + 0x100), vm::cast(ctxt->spurs.addr(), HERE), 128);
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
{
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
// Halt if already initialised
if (spurs->sysSrvOnSpu & (1 << ctxt->spuNum))
@@ -911,7 +904,7 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
spurs->sysSrvOnSpu |= 1 << ctxt->spuNum;
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
});
}//);
ctxt->traceBuffer = 0;
ctxt->traceMsgCount = -1;
@@ -937,7 +930,6 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
while (true)
{
CHECK_EMU_STATUS;
// Process requests for the system service
spursSysServiceProcessRequests(spu, ctxt);
@@ -980,7 +972,6 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
cellSpursModulePutTrace(&pkt, ctxt->dmaTagId);
spursSysServiceIdleHandler(spu, ctxt);
CHECK_EMU_STATUS;
goto poll;
}
@@ -993,9 +984,9 @@ void spursSysServiceProcessRequests(SPUThread& spu, SpursKernelContext* ctxt)
bool updateWorkload = false;
bool terminate = false;
vm::reservation_op(vm::cast(ctxt->spurs.addr() + OFFSET_32(CellSpurs, wklState1), HERE), 128, [&]()
//vm::reservation_op(vm::cast(ctxt->spurs.addr() + OFFSET_32(CellSpurs, wklState1), HERE), 128, [&]()
{
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
// Terminate request
if (spurs->sysSrvMsgTerminate & (1 << ctxt->spuNum))
@@ -1018,7 +1009,7 @@ void spursSysServiceProcessRequests(SPUThread& spu, SpursKernelContext* ctxt)
}
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
});
}//);
// Process update workload message
if (updateWorkload)
@@ -1072,9 +1063,9 @@ void spursSysServiceActivateWorkload(SPUThread& spu, SpursKernelContext* ctxt)
}
}
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
{
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++)
{
@@ -1129,7 +1120,7 @@ void spursSysServiceActivateWorkload(SPUThread& spu, SpursKernelContext* ctxt)
}
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
});
}//);
if (wklShutdownBitSet)
{
@@ -1144,9 +1135,9 @@ void spursSysServiceUpdateShutdownCompletionEvents(SPUThread& spu, SpursKernelCo
// workloads that have a shutdown completion hook registered
u32 wklNotifyBitSet;
u8 spuPort;
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
{
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
wklNotifyBitSet = 0;
spuPort = spurs->spuPort;;
@@ -1172,7 +1163,7 @@ void spursSysServiceUpdateShutdownCompletionEvents(SPUThread& spu, SpursKernelCo
}
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
});
}//);
if (wklNotifyBitSet)
{
@@ -1196,9 +1187,9 @@ void spursSysServiceTraceUpdate(SPUThread& spu, SpursKernelContext* ctxt, u32 ar
bool notify;
u8 sysSrvMsgUpdateTrace;
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
{
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
auto& trace = spurs->sysSrvTrace.raw();
sysSrvMsgUpdateTrace = trace.sysSrvMsgUpdateTrace;
@@ -1220,12 +1211,12 @@ void spursSysServiceTraceUpdate(SPUThread& spu, SpursKernelContext* ctxt, u32 ar
}
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
});
}//);
// Get trace parameters from CellSpurs and store them in the LS
if (((sysSrvMsgUpdateTrace & (1 << ctxt->spuNum)) != 0) || (arg3 != 0))
{
vm::reservation_acquire(vm::base(spu.offset + 0x80), ctxt->spurs.ptr(&CellSpurs::traceBuffer).addr(), 128);
//vm::reservation_acquire(vm::base(spu.offset + 0x80), ctxt->spurs.ptr(&CellSpurs::traceBuffer).addr(), 128);
auto spurs = vm::_ptr<CellSpurs>(spu.offset + 0x80 - OFFSET_32(CellSpurs, traceBuffer));
if (ctxt->traceMsgCount != 0xFF || spurs->traceBuffer.addr() == 0)
@@ -1261,9 +1252,9 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread& spu, SpursKernelContex
bool do_return = false;
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
{
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
if (spurs->sysSrvPreemptWklId[ctxt->spuNum] == 0xFF)
{
@@ -1275,15 +1266,15 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread& spu, SpursKernelContex
spurs->sysSrvPreemptWklId[ctxt->spuNum] = 0xFF;
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
});
}//);
if (do_return) return;
spursSysServiceActivateWorkload(spu, ctxt);
vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
//vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
{
auto spurs = ctxt->spurs.get_ptr_priv();
auto spurs = ctxt->spurs.get_ptr();
if (wklId >= CELL_SPURS_MAX_WORKLOAD)
{
@@ -1297,7 +1288,7 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread& spu, SpursKernelContex
}
std::memcpy(vm::base(spu.offset + 0x100), spurs, 128);
});
}//);
// Set the current workload id to the id of the pre-empted workload since cellSpursModulePutTrace
// uses the current worload id to determine the workload to which the trace belongs
@@ -1350,9 +1341,9 @@ bool spursTasksetEntry(SPUThread& spu)
ctxt->taskId = 0xFFFFFFFF;
// Register SPURS takset policy module HLE functions
spu.UnregisterHleFunctions(CELL_SPURS_TASKSET_PM_ENTRY_ADDR, 0x40000/*LS_BOTTOM*/);
spu.RegisterHleFunction(CELL_SPURS_TASKSET_PM_ENTRY_ADDR, spursTasksetEntry);
spu.RegisterHleFunction(ctxt->syscallAddr, spursTasksetSyscallEntry);
//spu.UnregisterHleFunctions(CELL_SPURS_TASKSET_PM_ENTRY_ADDR, 0x40000/*LS_BOTTOM*/);
//spu.RegisterHleFunction(CELL_SPURS_TASKSET_PM_ENTRY_ADDR, spursTasksetEntry);
//spu.RegisterHleFunction(ctxt->syscallAddr, spursTasksetSyscallEntry);
try
{
@@ -1442,9 +1433,9 @@ s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* is
s32 rc = CELL_OK;
s32 numNewlyReadyTasks;
vm::reservation_op(vm::cast(ctxt->taskset.addr(), HERE), 128, [&]()
//vm::reservation_op(vm::cast(ctxt->taskset.addr(), HERE), 128, [&]()
{
auto taskset = ctxt->taskset.get_ptr_priv();
auto taskset = ctxt->taskset.get_ptr();
// Verify taskset state is valid
be_t<v128> _0(v128::from32(0));
@@ -1601,12 +1592,12 @@ s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* is
taskset->ready = ready;
std::memcpy(vm::base(spu.offset + 0x2700), taskset, 128);
});
}//);
// Increment the ready count of the workload by the number of tasks that have become ready
vm::reservation_op(vm::cast(kernelCtxt->spurs.addr(), HERE), 128, [&]()
//vm::reservation_op(vm::cast(kernelCtxt->spurs.addr(), HERE), 128, [&]()
{
auto spurs = kernelCtxt->spurs.get_ptr_priv();
auto spurs = kernelCtxt->spurs.get_ptr();
s32 readyCount = kernelCtxt->wklCurrentId < CELL_SPURS_MAX_WORKLOAD ? spurs->wklReadyCount1[kernelCtxt->wklCurrentId].load() : spurs->wklIdleSpuCountOrReadyCount2[kernelCtxt->wklCurrentId & 0x0F].load();
readyCount += numNewlyReadyTasks;
@@ -1622,7 +1613,7 @@ s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* is
}
std::memcpy(vm::base(spu.offset + 0x100), spurs, 128);
});
}//);
return rc;
}
+55 -68
View File
@@ -6,10 +6,6 @@
#include "Emu/Cell/lv2/sys_process.h"
#include "cellSync.h"
#include "Emu/Memory/wait_engine.h"
#include <thread>
logs::channel cellSync("cellSync", logs::level::notice);
template<>
@@ -63,7 +59,7 @@ error_code cellSyncMutexInitialize(vm::ptr<CellSyncMutex> mutex)
return CELL_OK;
}
error_code cellSyncMutexLock(vm::ptr<CellSyncMutex> mutex)
error_code cellSyncMutexLock(ppu_thread& ppu, vm::ptr<CellSyncMutex> mutex)
{
cellSync.trace("cellSyncMutexLock(mutex=*0x%x)", mutex);
@@ -81,10 +77,10 @@ error_code cellSyncMutexLock(vm::ptr<CellSyncMutex> mutex)
const auto order = mutex->ctrl.atomic_op(&CellSyncMutex::lock_begin);
// Wait until rel value is equal to old acq value
vm::wait_op(mutex.addr(), 4, [&]
while (mutex->ctrl.load().rel != order)
{
return mutex->ctrl.load().rel == order;
});
ppu.test_state();
}
_mm_mfence();
@@ -129,8 +125,6 @@ error_code cellSyncMutexUnlock(vm::ptr<CellSyncMutex> mutex)
mutex->ctrl.atomic_op(&CellSyncMutex::unlock);
vm::notify_at(mutex.addr(), 4);
return CELL_OK;
}
@@ -159,7 +153,7 @@ error_code cellSyncBarrierInitialize(vm::ptr<CellSyncBarrier> barrier, u16 total
return CELL_OK;
}
error_code cellSyncBarrierNotify(vm::ptr<CellSyncBarrier> barrier)
error_code cellSyncBarrierNotify(ppu_thread& ppu, vm::ptr<CellSyncBarrier> barrier)
{
cellSync.trace("cellSyncBarrierNotify(barrier=*0x%x)", barrier);
@@ -173,9 +167,10 @@ error_code cellSyncBarrierNotify(vm::ptr<CellSyncBarrier> barrier)
return CELL_SYNC_ERROR_ALIGN;
}
vm::wait_op(barrier.addr(), 4, [&] { return barrier->ctrl.atomic_op(&CellSyncBarrier::try_notify); });
vm::notify_at(barrier.addr(), 4);
while (!barrier->ctrl.atomic_op(&CellSyncBarrier::try_notify))
{
ppu.test_state();
}
return CELL_OK;
}
@@ -201,12 +196,10 @@ error_code cellSyncBarrierTryNotify(vm::ptr<CellSyncBarrier> barrier)
return not_an_error(CELL_SYNC_ERROR_BUSY);
}
vm::notify_at(barrier.addr(), 4);
return CELL_OK;
}
error_code cellSyncBarrierWait(vm::ptr<CellSyncBarrier> barrier)
error_code cellSyncBarrierWait(ppu_thread& ppu, vm::ptr<CellSyncBarrier> barrier)
{
cellSync.trace("cellSyncBarrierWait(barrier=*0x%x)", barrier);
@@ -222,9 +215,10 @@ error_code cellSyncBarrierWait(vm::ptr<CellSyncBarrier> barrier)
_mm_mfence();
vm::wait_op(barrier.addr(), 4, [&] { return barrier->ctrl.atomic_op(&CellSyncBarrier::try_wait); });
vm::notify_at(barrier.addr(), 4);
while (!barrier->ctrl.atomic_op(&CellSyncBarrier::try_wait))
{
ppu.test_state();
}
return CELL_OK;
}
@@ -250,8 +244,6 @@ error_code cellSyncBarrierTryWait(vm::ptr<CellSyncBarrier> barrier)
return not_an_error(CELL_SYNC_ERROR_BUSY);
}
vm::notify_at(barrier.addr(), 4);
return CELL_OK;
}
@@ -284,7 +276,7 @@ error_code cellSyncRwmInitialize(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer,
return CELL_OK;
}
error_code cellSyncRwmRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
error_code cellSyncRwmRead(ppu_thread& ppu, vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
{
cellSync.trace("cellSyncRwmRead(rwm=*0x%x, buffer=*0x%x)", rwm, buffer);
@@ -299,7 +291,10 @@ error_code cellSyncRwmRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
}
// wait until `writers` is zero, increase `readers`
vm::wait_op(rwm.addr(), 8, [&] { return rwm->ctrl.atomic_op(&CellSyncRwm::try_read_begin); });
while (!rwm->ctrl.atomic_op(&CellSyncRwm::try_read_begin))
{
ppu.test_state();
}
// copy data to buffer
std::memcpy(buffer.get_ptr(), rwm->buffer.get_ptr(), rwm->size);
@@ -310,8 +305,6 @@ error_code cellSyncRwmRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
return CELL_SYNC_ERROR_ABORT;
}
vm::notify_at(rwm.addr(), 8);
return CELL_OK;
}
@@ -344,12 +337,10 @@ error_code cellSyncRwmTryRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
return CELL_SYNC_ERROR_ABORT;
}
vm::notify_at(rwm.addr(), 8);
return CELL_OK;
}
error_code cellSyncRwmWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
error_code cellSyncRwmWrite(ppu_thread& ppu, vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
{
cellSync.trace("cellSyncRwmWrite(rwm=*0x%x, buffer=*0x%x)", rwm, buffer);
@@ -364,10 +355,16 @@ error_code cellSyncRwmWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
}
// wait until `writers` is zero, set to 1
vm::wait_op(rwm.addr(), 8, [&] { return rwm->ctrl.atomic_op(&CellSyncRwm::try_write_begin); });
while (!rwm->ctrl.atomic_op(&CellSyncRwm::try_write_begin))
{
ppu.test_state();
}
// wait until `readers` is zero
vm::wait_op(rwm.addr(), 8, [&] { return rwm->ctrl.load().readers == 0; });
while (rwm->ctrl.load().readers != 0)
{
ppu.test_state();
}
// copy data from buffer
std::memcpy(rwm->buffer.get_ptr(), buffer.get_ptr(), rwm->size);
@@ -375,8 +372,6 @@ error_code cellSyncRwmWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
// sync and clear `readers` and `writers`
rwm->ctrl.exchange({ 0, 0 });
vm::notify_at(rwm.addr(), 8);
return CELL_OK;
}
@@ -406,8 +401,6 @@ error_code cellSyncRwmTryWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
// sync and clear `readers` and `writers`
rwm->ctrl.exchange({ 0, 0 });
vm::notify_at(rwm.addr(), 8);
return CELL_OK;
}
@@ -446,7 +439,7 @@ error_code cellSyncQueueInitialize(vm::ptr<CellSyncQueue> queue, vm::ptr<u8> buf
return CELL_OK;
}
error_code cellSyncQueuePush(vm::ptr<CellSyncQueue> queue, vm::cptr<void> buffer)
error_code cellSyncQueuePush(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::cptr<void> buffer)
{
cellSync.trace("cellSyncQueuePush(queue=*0x%x, buffer=*0x%x)", queue, buffer);
@@ -464,15 +457,16 @@ error_code cellSyncQueuePush(vm::ptr<CellSyncQueue> queue, vm::cptr<void> buffer
u32 position;
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_push_begin, depth, &position); });
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_push_begin, depth, &position))
{
ppu.test_state();
}
// copy data from the buffer at the position
std::memcpy(&queue->buffer[position * queue->size], buffer.get_ptr(), queue->size);
queue->ctrl.atomic_op(&CellSyncQueue::push_end);
vm::notify_at(queue.addr(), 8);
return CELL_OK;
}
@@ -504,12 +498,10 @@ error_code cellSyncQueueTryPush(vm::ptr<CellSyncQueue> queue, vm::cptr<void> buf
queue->ctrl.atomic_op(&CellSyncQueue::push_end);
vm::notify_at(queue.addr(), 8);
return CELL_OK;
}
error_code cellSyncQueuePop(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
error_code cellSyncQueuePop(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
{
cellSync.trace("cellSyncQueuePop(queue=*0x%x, buffer=*0x%x)", queue, buffer);
@@ -527,15 +519,16 @@ error_code cellSyncQueuePop(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
u32 position;
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_pop_begin, depth, &position); });
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_pop_begin, depth, &position))
{
ppu.test_state();
}
// copy data at the position to the buffer
std::memcpy(buffer.get_ptr(), &queue->buffer[position % depth * queue->size], queue->size);
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
vm::notify_at(queue.addr(), 8);
return CELL_OK;
}
@@ -567,12 +560,10 @@ error_code cellSyncQueueTryPop(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffe
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
vm::notify_at(queue.addr(), 8);
return CELL_OK;
}
error_code cellSyncQueuePeek(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
error_code cellSyncQueuePeek(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
{
cellSync.trace("cellSyncQueuePeek(queue=*0x%x, buffer=*0x%x)", queue, buffer);
@@ -590,15 +581,16 @@ error_code cellSyncQueuePeek(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
u32 position;
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_peek_begin, depth, &position); });
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_peek_begin, depth, &position))
{
ppu.test_state();
}
// copy data at the position to the buffer
std::memcpy(buffer.get_ptr(), &queue->buffer[position % depth * queue->size], queue->size);
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
vm::notify_at(queue.addr(), 8);
return CELL_OK;
}
@@ -630,8 +622,6 @@ error_code cellSyncQueueTryPeek(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buff
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
vm::notify_at(queue.addr(), 8);
return CELL_OK;
}
@@ -654,7 +644,7 @@ error_code cellSyncQueueSize(vm::ptr<CellSyncQueue> queue)
return not_an_error(queue->ctrl.load().count & 0xffffff);
}
error_code cellSyncQueueClear(vm::ptr<CellSyncQueue> queue)
error_code cellSyncQueueClear(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue)
{
cellSync.trace("cellSyncQueueClear(queue=*0x%x)", queue);
@@ -670,13 +660,18 @@ error_code cellSyncQueueClear(vm::ptr<CellSyncQueue> queue)
const u32 depth = queue->check_depth();
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_1); });
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_2); });
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_1))
{
ppu.test_state();
}
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_2))
{
ppu.test_state();
}
queue->ctrl.exchange({ 0, 0 });
vm::notify_at(queue.addr(), 8);
return CELL_OK;
}
@@ -848,8 +843,6 @@ error_code _cellSyncLFQueueGetPushPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQue
{
while (true)
{
CHECK_EMU_STATUS;
const auto old = queue->push1.load(); _mm_lfence();
auto push = old;
@@ -1091,8 +1084,6 @@ error_code _cellSyncLFQueuePushBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> qu
while (true)
{
CHECK_EMU_STATUS;
s32 res;
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
@@ -1111,7 +1102,7 @@ error_code _cellSyncLFQueuePushBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> qu
break;
}
std::this_thread::sleep_for(1ms); // hack
ppu.test_state();
}
const s32 depth = queue->m_depth;
@@ -1147,8 +1138,6 @@ error_code _cellSyncLFQueueGetPopPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueu
{
while (true)
{
CHECK_EMU_STATUS;
const auto old = queue->pop1.load(); _mm_lfence();
auto pop = old;
@@ -1390,8 +1379,6 @@ error_code _cellSyncLFQueuePopBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> que
while (true)
{
CHECK_EMU_STATUS;
s32 res;
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
@@ -1410,7 +1397,7 @@ error_code _cellSyncLFQueuePopBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> que
break;
}
std::this_thread::sleep_for(1ms); // hack
ppu.test_state();
}
const s32 depth = queue->m_depth;

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