Compare commits

...

366 commits

Author SHA1 Message Date
mitoposter
57ff99ce53
cubeb: Show default device option even if enumerating devices fails (#1515) 2025-03-19 17:06:55 +01:00
capitalistspz
8b5cafa98e
Wiimote/L2CAP: More accurate descriptions for descriptors (#1512) 2025-03-13 01:09:45 +01:00
Crementif
186e92221a
debugger: allow printing registers using logging breakpoint placeholders (#1510)
This allows a savy user, developer or modder to change the comment field of a logging breakpoint to include placeholders such as {r3} or {f3} to log the register values whenever that code is hit.
2025-03-07 23:40:17 +01:00
goeiecool9999
31d2db6f78 OpenGL: Add explicit/matching qualifiers in output shader interface
fixes issues with old intel drivers
2025-03-05 22:23:06 +01:00
capitalistspz
ebb5ab53e2
Add menu item for opening shader cache directory (#1494) 2025-02-14 20:56:51 +01:00
capitalistspz
a6fb0a48eb
BUILD.md: Provide more info about build configuration flags (#1486) 2025-02-04 10:56:33 +01:00
Exzap
ec2d7c086a coreinit: Clean up time functions 2025-01-30 03:49:17 +01:00
Exzap
c714e8cb6b coreinit: Time to tick conversion is unsigned
The result is treated as signed in most cases, but the calculation uses unsigned arithmetic.

As a concrete example where this matters, DS VC passes -1 (2^64-1) to OSWaitEventWithTimeout which internally causes an overflow. But only with unsigned arithmetic this will result in a large positive number that behaves like the intended infinite timeout. With signed arithmetic the result is negative and the events will timeout immediately.
2025-01-30 03:32:24 +01:00
goeiecool9999
e834515f43
Vulkan: Improve post-shutdown cleanup and minor improvements (#1401) 2025-01-23 21:20:03 +01:00
Exzap
4f9eea07e0 CI: Update action version 2025-01-23 21:06:07 +01:00
goeiecool9999
372c314f06 fix building with fmt11 and GCC 2025-01-23 21:03:11 +01:00
Exzap
5bd253a1f8 Revert "Fix building against fmt 11.1.0 (#1474)"
Reverting commit 4ac65159ef because game profile enums use the stringifying formatters from config.h and are not supposed to store raw integers
2025-01-23 17:33:06 +01:00
Alexandre Bouvier
4ac65159ef
Fix building against fmt 11.1.0 (#1474) 2025-01-16 12:54:29 +01:00
Joshua de Reeper
eab1b24320
nsyshid: Initialise interface index as 0 (#1473) 2025-01-12 20:20:48 +01:00
Exzap
07cd402531
Update precompiled.h 2025-01-12 18:33:15 +01:00
Joshua de Reeper
0a59085021
nsyshid: Make Libusb the Windows backend (#1471) 2025-01-12 14:33:24 +01:00
Exzap
8dd809d725
Latte: Implement better index caching (#1443) 2025-01-12 12:39:02 +01:00
rcaridade145
1923b7a7c4
Vulkan: Added R5_G6_B5_UNORM to supported readback formats (#1430) 2025-01-12 12:37:56 +01:00
brysma1
f61539a262
Update build instructions for fedora and add troubleshooting step for alternative architectures (#1468) 2025-01-08 04:22:55 +01:00
Crementif
92021db230
Use one CPU emulation thread for --force-interpreter (#1467) 2025-01-05 04:08:13 +01:00
Crementif
4b792aa4d2
debug: Fix shader dumping (#1466) 2025-01-04 20:38:42 +01:00
capitalistspz
1e30d72658
build: Add ALLOW_PORTABLE flag (#1464)
* Add ALLOW_PORTABLE cmake flag
* Also check that `portable` is a directory
2024-12-30 18:49:51 +01:00
Mike Lothian
2b0cbf7f6b
Fix building against Boost 1.87.0 (#1455) 2024-12-18 22:15:42 +01:00
goeiecool9999
3738ccd2e6
Play bootSound.btsnd while shaders/pipelines are compiling (#1047) 2024-12-18 15:55:23 +01:00
Exzap
b53b223ba9 Vulkan: Use cache for sampler objects 2024-12-16 13:05:22 +01:00
Exzap
6aaad1eb83 Debugger: Added right click context menu to disasm view + small fixes 2024-12-16 13:05:22 +01:00
Exzap
adab729f43 UI: Correctly handle unicode paths during save export 2024-12-16 13:05:22 +01:00
capitalistspz
dd0af0a56f
Linux: Allow connecting Wiimotes via L2CAP (#1353) 2024-12-07 12:02:40 +01:00
Exzap
934cb54605 Properly check if MLC is writeable 2024-12-07 10:26:17 +01:00
Exzap
356cf0e5e0 Multiple smaller HLE improvements 2024-12-07 10:26:17 +01:00
Exzap
e2d0871ca3 Camera: Set error code in CAMInit
Fixes Hunter's Trophy 2 crashing on boot
2024-12-07 10:26:17 +01:00
Cemu-Language CI
40d9664d1c Update translation files 2024-12-07 07:14:20 +00:00
neebyA
eca7374567
Set version for macOS bundle (#1431) 2024-12-02 05:19:15 +01:00
Jeremy Kescher
80a6057512
build: Fix linker failure with glslang 15.0.0 (#1436) 2024-12-02 01:01:22 +01:00
capitalistspz
0735237686
Input: Move pairing dialog button and source (#1424) 2024-11-30 23:05:50 +01:00
capitalistspz
90eb2e01f4
nsyshid/dimensions: add missing return (#1425) 2024-11-22 13:43:12 +01:00
Exzap
409f12b13a coreinit: Fix calculation of thread total awake time 2024-11-21 20:34:24 +01:00
Exzap
7b513f1744 Latte: Add workaround for infinite loop in Fatal Frame shaders 2024-11-21 20:34:24 +01:00
Exzap
c3e29fb619 Latte: Add support for shader instructions MIN_UINT and MAX_UINT
Seen in the eShop version of Fatal Frame
Also made some warnings less spammy since this game seems to trigger it a lot
2024-11-21 20:34:24 +01:00
Exzap
2065ac5f63 GfxPack: Better logging messages for diagnosing problems in rules.txt 2024-11-21 20:34:24 +01:00
goeiecool9999
269d5b9aab
Vulkan: Make scaling shaders compatible + fixes (#1392) 2024-11-16 10:02:43 +01:00
Exzap
6f9f3d52ea CI: Remove outdated workflow 2024-11-13 06:38:17 +01:00
Exzap
719c631f13 config: Fix receive_untested_updates using the wrong default 2024-11-13 06:29:24 +01:00
Exzap
66658351c1 erreula: Rework implementation and fix bugs
- ErrEula doesn't disappear on its own anymore. The expected behavior is for the game to call Disappear once a button has been selected. This fixes issues where the dialog would softlock in some games
- Modernized code a bit
- Added a subtle fade in/out effect
2024-11-13 06:29:24 +01:00
Exzap
a5717e1b11 FST: Refactoring to fix a read bug + verify all reads
- Fixes a bug where corrupted data would be returned when reading files from unhashed sections with non-block aligned offset or size
- Added hash checks for all reads where possible. This means that FST now can automatically catch corruptions when they are encountered while reading from the volume
2024-11-13 06:29:23 +01:00
Joshua de Reeper
ca2e0a7c31
nsyshid: Add support for emulated Dimensions Toypad (#1371) 2024-11-11 08:58:01 +01:00
capitalistspz
2e829479d9
nsyshid/libusb: correct error message formatting and print error string on open fail (#1407) 2024-11-09 06:22:13 +01:00
capitalistspz
4ac1ab162a
procui: swap tickDelay and priority args in callbacks (#1408) 2024-11-09 06:21:06 +01:00
SamoZ256
813f9148b1
macOS: Fix absolute path to libusb dylib (#1405) 2024-11-07 07:09:35 +01:00
SamoZ256
9941e00b54
macOS: Fix libusb path for bundle (#1403) 2024-11-05 22:22:00 +01:00
Exzap
1c49a8a1ba nn_nfp: Implement GetNfpReadOnlyInfo and fix deactivate event
Fixes Amiibos not being detected in MK8
2024-11-01 22:47:19 +01:00
capitalistspz
47001ad233
Make MEMPTR<T> a little more T*-like (#1385) 2024-10-30 23:10:32 +01:00
goeiecool9999
459fd5d9bb
input: Fix crash when closing add controller dialog before search completes (#1386) 2024-10-28 09:37:30 +01:00
capitalistspz
63e1289bb5
Windows: Save icons to Cemu user data directory (#1390) 2024-10-25 18:48:21 +02:00
goeiecool9999
f9a4b2dbb1
input: Add option to make show screen button a toggle (#1383) 2024-10-19 01:56:56 +02:00
goeiecool9999
d6575455ee Linux: Fix crash on invalid command-line arguments
use std::cout instead of wxMessageBox which does not work when wxWidgets has not been initialised yet
2024-10-17 22:24:20 +02:00
goeiecool9999
3acd0c4f2c
Vulkan: Protect against uniform var ringbuffer overflow (#1378) 2024-10-14 14:03:36 +02:00
Alexandre Bouvier
6dc73f5d79
Add support for fmt 11 (#1366) 2024-10-03 08:48:25 +02:00
capitalistspz
8508c62540
Various smaller code improvements (#1343) 2024-09-17 02:00:26 +02:00
Andrea Toska
adffd53dbd
boss: Fix BOSS not honoring the proxy_server setting (#1344) 2024-09-16 12:40:38 +02:00
goeiecool9999
a05bdb172d
Vulkan: Add explicit synchronization on frame boundaries (#1290) 2024-09-15 20:23:11 +02:00
Cemu-Language CI
1a4d9660e7 Update translation files 2024-09-08 15:40:13 +00:00
Exzap
ba54d1540c Fix "Receive untested updates" option not being synced to config 2024-09-08 17:21:20 +02:00
MoonlightWave-12
0d8fd7c0dc
appimage: Do not copy libstdc++.so.6 to usr/lib/ (#1319) 2024-09-02 21:22:38 +02:00
Joshua de Reeper
b06990607d
nsysnet: Avoid crash on NULL timeout in select (#1324) 2024-09-02 16:20:16 +02:00
squidbus
9a53b19403
CI+build: Improve macOS builds (#1310) 2024-08-28 11:06:49 +02:00
Cemu-Language CI
03484d2146 Update translation files 2024-08-28 09:05:50 +00:00
Exzap
1234e2c118
Preparations for 2.1 (#1306) 2024-08-26 11:43:38 +02:00
Cemu-Language CI
d7f39aab05 Update translation files 2024-08-26 09:16:11 +00:00
bl
dc9d99b03b
nn_fp: Implement GetMyComment and UpdateCommentAsync (#1173) 2024-08-24 21:03:03 +02:00
Exzap
573c98b2f8 GfxPack: Workaround for invisible detail panel
Fixes #1307
There is probably a better way to calculate the maximum width. But this suffices for now as a workaround
2024-08-23 19:26:33 +02:00
Cemu-Language CI
9e53c1ce27 Update translation files 2024-08-22 05:17:01 +00:00
Exzap
958137a301 vpad: Keep second channel empty if no extra GamePad is configured 2024-08-15 18:27:08 +02:00
20943204920434
294a6de779
Update appimage.sh to support runtime libstdc++.so.6 loading (#1292)
Add checkrt plugin in order to detect the right libstdc++.so.6 version to load.
2024-08-15 16:22:41 +02:00
Exzap
2843da4479 padscore: Invoke sampling callbacks every 5ms
This fixes high input latency in games like Pokemon Rumble U which update input via the sampling callbacks
2024-08-15 05:00:09 +02:00
Exzap
b0bab273e2 padscore: Simulate queue behaviour for KPADRead 2024-08-15 02:16:24 +02:00
Skyth (Asilkan)
c49296acdc
Add support for iterating directories in graphics pack content folders. (#1288) 2024-08-13 15:53:04 +02:00
goeiecool9999
a6d8c0fb9f
CI: Fix macOS build (#1291) 2024-08-13 15:48:13 +02:00
Exzap
e551f8f524 Fix clang compile error 2024-08-13 05:57:55 +02:00
Exzap
f52970c822 Vulkan: Allow RGBA16F texture format with SRGB bit 2024-08-13 04:47:43 +02:00
Exzap
e02cc42d67 COS: Implement PPC va_list, va_arg and update related functions 2024-08-13 01:00:56 +02:00
goeiecool9999
9812a47cb1
clang-format: Put class braces on a new line (#1283) 2024-08-08 19:35:50 +02:00
Exzap
7fd532436d CI: Manual unshallow of vcpkg is no longer needed 2024-08-08 16:07:45 +02:00
Exzap
598298cb3d Vulkan: Fix stencil front mask 2024-08-08 16:07:45 +02:00
goeiecool9999
54e695a6e8
git: unshallow vcpkg, shallow vulkan-headers and imgui (#1282) 2024-08-08 15:58:24 +02:00
Exzap
bf2208145b Enable async shader compile by default 2024-08-07 16:35:15 +02:00
Exzap
b52b676413 vcpkg: Automatically unshallow submodule 2024-08-07 02:50:24 +02:00
Exzap
2129644781 Remove shaderCache directory
The location of the shaderCache path is different for non-portable cases so let's not confuse the user by shipping with a precreated directory that isn't actually used
2024-08-06 23:02:28 +02:00
Exzap
d81eb952a4 nsyshid: Silence some logging in release builds 2024-08-06 22:58:23 +02:00
Exzap
1575866eca Vulkan: Add R32_X8_FLOAT format 2024-08-04 14:47:51 +02:00
Joshua de Reeper
517e68fe57
nsyshid: Tidyups and Fixes (#1275) 2024-07-28 18:50:20 +02:00
Exzap
593da5ed79 CI: Workaround for MoltenVK crash
1.2.10 and later crash during descriptor set creation. So for now let's stick with the older version
2024-07-27 18:35:23 +02:00
Exzap
c73fa3761c Fix compatibility with GCC 2024-07-27 04:45:36 +02:00
Exzap
5328e9eb10 CPU: Fix overflow bit calculation in SUBFO instruction
Since rD can overlap with rA or rB the result needs to be stored in a temporary
2024-07-26 06:03:00 +02:00
Exzap
47f1dcf996 debugger: Add symbol support to PPC stack traces
Also moved the declaration to precompiled.h instead of redefining it wherever it is used
2024-07-26 06:03:00 +02:00
Exzap
252429933f debugger: Slightly optimize symbol list updates 2024-07-26 06:03:00 +02:00
Exzap
026d547dcc Use HTTP 1.1 in Nintendo API requests 2024-07-26 06:03:00 +02:00
Exzap
f1685eab66
h264: Use asynchronous decoding when possible (#1257) 2024-07-26 05:48:42 +02:00
Exverge
4b9c7c0d30
Update Fedora build instructions (#1269) 2024-07-24 08:32:40 +02:00
capitalistspz
e65abf4898
Suppress unnecessary GTK messages (#1267) 2024-07-23 22:18:55 +02:00
Joshua de Reeper
a1c1a608d7
nsyshid: Emulate Infinity Base (#1246) 2024-07-23 03:18:48 +02:00
Exzap
64232ffdbd
Windows default to non-portable + Reworked MLC handling and related UI (#1252) 2024-07-23 03:13:36 +02:00
goeiecool9999
7522c8470e
resource: move fontawesome to .rodata (#1259) 2024-07-19 14:24:46 +02:00
Exzap
9d366937cd Workaround for compiler issue with Visual Studio 17.10 2024-07-07 08:55:26 +02:00
Joshua de Reeper
5209677f2f
nsyshid: Add SetProtocol and SetReport support for libusb backend (#1243) 2024-07-02 03:32:37 +02:00
Colin Kinloch
64b0b85ed5
Create GamePad window at correct size (#1247)
Don't change the size on canvas initialization
2024-06-29 22:31:47 +02:00
Joshua de Reeper
aefbb918be
nsyshid: Skylander emulation fixes and code cleanup (#1244) 2024-06-28 15:44:49 +02:00
Joshua de Reeper
93b58ae6f7
nsyshid: Add infrastructure and support for emulating Skylander Portal (#971) 2024-06-28 00:55:20 +02:00
Exzap
f3d20832c1 Avoid an unhandled exception when mlc path is invalid 2024-06-25 19:28:21 +02:00
Exzap
d4c2c3d209 nsyskbd: Stub KBDGetKey
Fixes MSX VC games freezing on boot
2024-06-25 15:50:06 +02:00
Exzap
1672f969bb Latte: Add support for vertex format used by Rabbids Land 2024-06-09 17:53:34 +02:00
goeiecool9999
6772b1993f
vcpkg: Update dependencies (#1229) 2024-06-05 16:34:42 +02:00
Exzap
16070458ed Logging: Restructure menu + allow toggeling APIErrors logtype
The logtype "APIErrors" previously was always enabled. This option is intended to help homebrew developers notice mistakes in how they use CafeOS API. But some commercial games trigger these a lot and cause log.txt bloat (e.g. seen in XCX). Thus this commit changes it so that it's off by default and instead can be toggled if desired.

Additionally in this commit:
- COS module logging options are no longer translatable (our debug logging is fundamentally English)
- Restructured the log menu and moved the logging options that are mainly of interest to Cemu devs into a separate submenu
2024-06-02 21:39:40 +02:00
Exzap
5f825a1fa8 Latte: Always allow views with the same format as base texture
Fixes crash/assert in VC N64 titles
2024-06-02 21:39:40 +02:00
Colin Kinloch
d33337d539
Fix GamePad window size (#1224) 2024-05-29 00:36:12 +02:00
Exzap
f576269ed0 Refactor legacy method of emulating thread events 2024-05-29 00:34:11 +02:00
Exzap
da8fd5b7c7 nn_save: Refactor and modernize code 2024-05-29 00:07:37 +02:00
Exzap
1ee9d5c78c coreinit: Tweak JD2019 workaround to avoid XCX softlock 2024-05-27 01:24:24 +02:00
goeiecool9999
aadd2f4a1a
Input: Assign profile name correctly on save (#1217) 2024-05-25 01:48:53 +02:00
qurious-pixel
149fe10a4e
CI+MacOS: Use libusb dylib from vcpkg (#1219) 2024-05-25 01:48:17 +02:00
Cemu-Language CI
917ea2ef23 Update translation files 2024-05-23 17:48:04 +00:00
Exzap
b048a1fd9e Use CURLOPT_USERAGENT instead of manually setting User-Agent 2024-05-22 05:08:03 +02:00
Exzap
a059338890
Add initial NTAG and NFC implementation 2024-05-22 04:45:10 +02:00
goeiecool9999
523a1652df
OpenGL: Restore ProgramBinary cache for GL shaders (#1209) 2024-05-22 04:23:33 +02:00
goeiecool9999
c913a59c7a
TitleList: Add homebrew title type (#1203) 2024-05-22 04:11:02 +02:00
GaryOderNichts
964d2acb44 Filestream_unix: Include cstdarg 2024-05-18 20:47:09 +02:00
GaryOderNichts
a115921b43 Fix inconsistency with int types 2024-05-18 20:37:37 +02:00
GaryOderNichts
eb1983daa6 nfc: Remove backup path 2024-05-18 20:37:37 +02:00
GaryOderNichts
8fe69cd0fb Properly implement NFC result codes 2024-05-18 20:37:37 +02:00
GaryOderNichts
41fe598e33 nfc: Implement UID filter 2024-05-18 20:37:37 +02:00
GaryOderNichts
8e8431113a ntag: Implement NTAGWrite 2024-05-18 20:37:37 +02:00
GaryOderNichts
1c6b209692 Add initial ntag and nfc implementation 2024-05-18 20:37:37 +02:00
GaryOderNichts
84e78088fb PPCCoreCallback: Add support for stack args if GPR limit is reached 2024-05-18 20:37:37 +02:00
splatoon1enjoyer
13b90874f9
Fix commas edge case in strings when parsing an assembly line (#1201) 2024-05-13 16:52:25 +02:00
Exzap
cf41c3b136
CI: Use submodule commit of vcpkg 2024-05-10 09:33:32 +02:00
Xphalnos
97d8cf4ba3
vcpkg: Update libraries (#1198) 2024-05-10 09:32:06 +02:00
GaryOderNichts
b2a6cccc89
nn_act: Implement GetTransferableId (#1197) 2024-05-09 12:12:34 +02:00
GaryOderNichts
10d553e1c9
zlib125: Implement deflateInit_ (#1194) 2024-05-07 11:56:28 +02:00
Exzap
3f8722f0a6 Track online-enable and network-service settings per-account instead of globally 2024-05-06 18:18:42 +02:00
Exzap
065fb7eb58 coreinit: Add reschedule special case to avoid a deadlock
Fixes Just Dance 2019 locking up on boot
2024-05-06 09:15:36 +02:00
capitalistspz
7d6d417354
Input: Improve setting of dpd_enable_fg (#1127) 2024-05-06 03:27:30 +02:00
Exzap
bd13d4bdc3 nn_act: Make AcquireToken gracefully fail in offline mode + refactor 2024-05-05 17:05:11 +02:00
Exzap
bf37a8281e CI: Update action versions 2024-05-05 14:06:26 +02:00
Exzap
dd3ed56509 nn_save: Fix inverted condition preventing accessing other title's saves 2024-05-05 10:05:35 +02:00
Exzap
70afe3a033 nlibcurl: Use separte logging type 2024-05-05 09:11:08 +02:00
goeiecool9999
dc480ac00b
Add support for WUHB file format (#1190) 2024-05-05 02:35:01 +02:00
qurious-pixel
f28043e0e9
Linux/Mac Auto-Updater (#1145) 2024-05-05 01:34:36 +02:00
Exzap
a744670486 coreinit: Add export for OSGetForegroundBucketFreeArea 2024-05-05 01:33:15 +02:00
Exzap
48d2a8371b sndcore: Write log message instead of asserting in AXSetDeviceRemixMatrix
Fixes a crash in Watch Dogs due to the non-debug assert
2024-05-05 01:33:15 +02:00
Exzap
91a010fbdd proc_ui: Fix crash due to incorrect version handling
Resolves a crash in NEX Remix
2024-05-04 08:05:10 +02:00
Exzap
a16c37f0c5 coreinit: Rework thread creation
New implementation is much closer to console behavior. For example we didn't align the stack which would cause crashes in the Miiverse applet
2024-05-04 07:05:59 +02:00
Exzap
041f29a914 nn_act: Implement GetTimeZoneId placeholder 2024-05-03 02:44:10 +02:00
Exzap
1b5c885621 nn_acp: Implement ACPGetTitleMetaXml 2024-05-03 02:41:39 +02:00
Exzap
c11d83e9d8 coreinit: Implement MCP_GetTitleId 2024-05-03 02:41:05 +02:00
Exzap
379950d185 coreinit+nn_save: Cleanup some legacy code 2024-05-01 05:06:50 +02:00
Exzap
e7c6862e19 DownloadManager: Fix missing updates 2024-05-01 01:55:55 +02:00
Exzap
1c73dc9e1b Implement proc_ui.rpl + stub SYSSwitchToEManual() to avoid softlocks
- Full reimplementation of proc_ui.rpl with all 19 exports
- Foreground/Background messages now go to the coreinit system message queue as they should (instead of using a hack where proc_ui receives them directly)
- Add missing coreinit API needed by proc_ui: OSGetPFID(), OSGetUPID(), OSGetTitleID(), __OSCreateThreadType()
- Use big-endian types in OSMessage
- Flesh out the stubs for OSDriver_Register and OSDriver_Unregister a bit more since we need to call it from proc_ui. Similiar small tweaks to other coreinit API
- Stub sysapp SYSSwitchToEManual() and _SYSSwitchToEManual() in such a way that they will trigger the expected background/foreground transition, avoiding softlocks in games that call these functions
2024-04-30 23:29:15 +02:00
Exzap
c038e758ae IOSU: Clean up resource on service shutdown
Also set device-dependent thread name
2024-04-30 23:19:11 +02:00
Exzap
b2be3c13df Add example network_services.xml 2024-04-30 23:19:04 +02:00
GaryOderNichts
fdf239929f
nsysnet: Various improvements (#1188)
- Do not raise an assert for unimplemented optnames
- recvfrom: src_addr and addrlen can be NULL
- getsockopt: Implement SO_TYPE
2024-04-29 00:24:43 +02:00
goeiecool9999
5be98da0ac
OpenGL: Fix a crash when GL_VERSION is null (#1187) 2024-04-27 15:49:49 +02:00
Exzap
efbbb817fe DownloadManager: Always use Nintendo servers + additional streamlining
- Download manager now always uses Nintendo servers. Requires only a valid OTP and SEEPROM dump so you can use it in combination with a Pretendo setup even without a NNID
- Account drop down removed from download manager since it's not required
- Internally all our API requests now support overriding which service to use
- Drop support for act-url and ecs-url command line parameters. Usage of network_services.xml ("custom" option in the UI) is preferred
2024-04-20 12:19:06 +02:00
Exzap
989e2b8c8c prudp: More code cleanup + fix compile error 2024-04-18 23:11:39 +02:00
Exzap
e2f9725719 prudp: Code cleanup 2024-04-18 19:23:00 +02:00
Exzap
ee36992bd6 prudp: Improve ping and ack logic
Fixes the issue where the friend service connection would always timeout on Pretendo servers

The individual changes are:
- Outgoing ping packets now use their own incrementing sequenceId (matches official NEX behavior)
- If the server sends us a ping packet with NEEDS_ACK, we now respond
- Misc smaller refactoring and code clean up
- Added PRUDP as a separate logging option
2024-04-18 19:23:00 +02:00
goeiecool9999
10c78eccce
CI: don't strip debug symbols from binary in AppImage (#1175) 2024-04-15 05:20:39 +02:00
Exzap
6ea42d958c nlibcurl: Fix compile error 2024-04-13 11:03:02 +02:00
Exzap
9c28a728e4 prudp: Dont expect sessionId to match for PING+ACK
Fixes friend service connection periodically timing-out on Pretendo.

Seems that unlike Nintendo's servers, Pretendo doesn't set sessionId for PING ack packets.
2024-04-13 10:43:21 +02:00
Exzap
d5a8530246 nlibcurl: Detect invalid header combo + refactoring
Fixes error 106-0526 when opening course world on Super Mario Maker

Manually attaching Content-Length header for POST requests is undefined behavior on recent libcurl.
To detect the bad case some refactoring was necessary. In general we should try to move away from directly forwarding curl_easy_setopt() to the underlying instance as the behavior is diverging in modern libcurl. Much more refactoring work is required in the future to fix all of this.
2024-04-13 10:43:21 +02:00
goeiecool9999
84cad8b280
Vulkan: Remove unecessary present fence (#1166) 2024-04-11 06:41:57 +02:00
qurious-pixel
391533dbe5
Gamelist: Enable icon column by default (#1168) 2024-04-11 06:08:26 +02:00
goeiecool9999
bac1ac3b49
CI: use last vcpkg compatible CMake 3.29.0 (#1167) 2024-04-11 03:06:36 +02:00
Exzap
d45c2fa6d1 erreula: Avoid triggering debug assert in imgui
It does not like empty window titles
2024-04-10 20:23:15 +02:00
Exzap
12eda10387 nn_acp: Implement ACPGetOlvAccesskey + code clean up
Added ACPGetOlvAccesskey() which is used by Super Mario Maker

iosu acp, nn_acp and nn_save all cross talk with each other and are mostly legacy code. Modernized it a tiny bit and moved functions to where they should be. A larger refactor should be done in the future but for now this works ok
2024-04-10 20:22:27 +02:00
47463915
33a74c2035
nn_nfp: Avoid current app from showing up as "???" for others in Friend List + View friends' status (#1157) 2024-04-09 00:33:50 +02:00
Maschell
7b635e7eb8
nn_boss: Implement startIndex parameter usage in nn:boss:::GetDataList (#1162) 2024-04-08 19:51:30 +02:00
Maschell
9b30be0258
drmapp: Stub more functions to allow title loading from Wii U Menu (#1161) 2024-04-08 19:50:57 +02:00
Maschell
efbf712305
nn_sl: Stub GetDefaultWhiteListAccessor__Q2_2nn2slFv to avoid crash in Wii U Menu when an online account is used (#1159) 2024-04-08 19:15:49 +02:00
Exzap
74e8d205b0 coreinit: Handle SD mounting permission in FSGetMountSource
One Piece requires this to not get stuck in an infinite loop on boot.

This also sets up initial infrastructure for handling cos.xml permissions
2024-04-06 22:18:38 +02:00
Exzap
fde7230191 vcpkg/windows/mac: Avoid dependency on liblzma via tiff 2024-04-06 22:18:38 +02:00
goeiecool9999
075eac626b
ELF: Fix crash due to not allocating recompiler ranges (#1154) 2024-04-06 22:13:19 +02:00
Exzap
85141f17f9 vcpkg/linux: Avoid dependency on libsystemd/liblzma
libsystemd which is required by dbus has an optional dependency on liblzma and since we don't need it we can just strip it out of dbus
2024-04-03 15:40:38 +02:00
Exzap
5c0d5a54ac vcpkg/linux: Avoid dependency on liblzma for now
Use port of tiff which does not rely on lzma
2024-04-03 02:40:41 +02:00
Exzap
51072b510c nn_boss: Large rework with various improvements
Lots of internal changes. On the surface this only fixes a crash in Mario & Sonic Rio 2016 (at least what I saw from my testing) but it may affect more games.

Summary of changes:
- Rewrite code to use newer cafeExportRegisterFunc
- Simplify code by merging namespaces and structs of the same types
- Correctly set ppc vtables for the virtual boss classes
- Fix some wrong function definitions and implement a little bit more of the boss API (mainly constructors and destructors)
2024-04-03 01:56:49 +02:00
Exzap
3e467e220e Logging: Prevent crash for nullptr strings 2024-04-03 01:56:49 +02:00
Exzap
fa8bab2f39 Latte: Add support for LOOP_START_NO_AL shader instruction
This instruction is used by Injustice: Gods Among Us and Project Zero

Also improved robustness of rendering to be less prone to crashing when a game tries to draw with broken shaders
2024-04-03 01:56:49 +02:00
Exzap
60adc38205 Latte: Add support for more fence conditions
MEM_OP_GREATER is required by Injustice: Gods Among Us
2024-04-03 01:56:49 +02:00
Exzap
b0b2c25762 coreinit: Improve accuracy of OSSwitchCoroutine
Fixes Injustice: Gods Among Us crashing during boot.
2024-04-03 01:56:48 +02:00
goeiecool9999
5230fcab37
Debugger: Fix infinite loop in symbol storage (#1134) 2024-03-27 11:14:01 +01:00
goeiecool9999
4f3d4624f5
GraphicPacksWindow: Disable update button when a game is running (#1137) 2024-03-26 13:09:24 +01:00
goeiecool9999
111e383d1b
coreinit: Fix race condition that causes crash (#1138) 2024-03-26 13:07:08 +01:00
SSimco
fa4ad9b8c1
Gamelist: Add option to hide the icon column (#604) 2024-03-25 22:30:39 +01:00
Exzap
4b7d2f88ae Latte: Enable colorbuffer optimization if gfx packs are aware
The optimization for colorbuffer resolution introduced in PR #706 is now enabled. This optimization changes the resolution of certain framebuffer textures, which may conflict with the texture resolution rules set by some graphic packs. As a result, if a graphic pack that specifies texture resolution rules is in use, the optimization will automatically be turned off to prevent any issues.

To circumvent this, graphic packs can now include the setting "colorbufferOptimizationAware = true" in their rules.txt. This setting indicates that the pack has been updated to handle the resolution changes introduced by the optimization. Cemu will allow the optimization to remain enabled if resolution packs have this flag set.
2024-03-25 21:35:38 +01:00
Francesco Saltori
4d148b3696
Add supported locales to macOS plist (#1133) 2024-03-25 21:34:40 +01:00
capitalistspz
241915e1a6
Gamelist: Display title long names + improvements for shortcuts (#1126)
- Windows icons are stored as .ico files to %LOCALAPPDATA%/Cemu/icons/
- Long title names chosen as some games (NSMBU + NSLU) add trailing dots for their shortnames
- Long title names have their newlines replaced with spaces at parsing
- Linux shortcut paths are saved with UTF-8 encoding
- Game titles are copied and saved with UTF-8 encoding
2024-03-24 11:11:18 +01:00
goeiecool9999
17060752b6
Vulkan: Several swapchain fixes and refactors (#1132) 2024-03-24 10:57:08 +01:00
goeiecool9999
4d609f06b8
InputSettings: Fix controller type counter to restore WPAD limit (#1118) 2024-03-20 10:22:48 +01:00
goeiecool9999
42d14eec96
Minor code improvements (#1124) 2024-03-18 09:18:02 +01:00
goeiecool9999
eaa82817dd
Update thread names (#1120) 2024-03-15 23:06:48 +01:00
Exzap
731713de3a OpenGL: Remove "-legacy" flag
"Intel legacy mode" was a special mode to workaround various Intel OpenGL driver limitations during the earlier years of Cemu. It's been unmaintained for years and no longer serves a purpose.

If we ever bring back compatibility with ancient Intel GPUs it should be done in a more structured way than a blunt yes/no flag.
2024-03-14 03:11:04 +01:00
Exzap
193767e6cc Latte+Vulkan: Code cleanup
Besides a general cleanup:
- Remove deprecated resource destruction queues
- Move functionality from renderer into Latte base classes to deduplicate code
2024-03-14 01:10:52 +01:00
Exzap
bc04662525 Latte+GL+VK: Improve handling of gfx pack texture overwrite format
Graphic packs can overwrite the format of a texture (e.g. for higher bitdepth to lessen banding) but the code for this wasn't correctly working anymore.

- Fixes overwrite format being ignored for texture views on Vulkan backend
- Fixes overwrite format not being used for texture views on OpenGL

Format aliasing is complicated enough as it is, even without overwrites, so this adds a new rule to make behavior more well defined: If two textures share memory but only one uses an overwrite format, then they are no longer synchronized and are considered separate textures.

Bonus fixes for OpenGL:
- Use fbo 0 instead of -1 as the default. This silences some warnings in debug output
- On OpenGL, bind new framebuffers on handle generation so they are considered created
2024-03-13 02:41:42 +01:00
Exzap
8bc444bb97 Latte: Derive framebuffer size from correct mip of depth buffer 2024-03-12 16:16:52 +01:00
Exzap
6fa77feba3 Latte: Fix regression in dd7cb74 2024-03-12 05:53:31 +01:00
Exzap
224866c3d2 CI: Work around a vcpkg issue by checking out an earlier commit 2024-03-12 01:43:52 +01:00
Exzap
a50e25300d Vulkan: Remove unused code path for texture copies
In 2020 we switched to drawcalls for texture copies replacing the copy-via-buffer path. It's not been used since so lets remove it
2024-03-11 23:01:37 +01:00
Exzap
1f9b89116f Vulkan: Fix crash during shutdown if shaders are still compiling
Make sure the async shader compiler threads are stopped before the shaders are deleted
2024-03-11 21:57:37 +01:00
Exzap
40d1eaeb72 nn_ac: Refactor and implement more API
Doesn't fix any issue as far as I know but it removes some of the unsupported API complaints in debug logging
2024-03-11 21:57:37 +01:00
Exzap
dd7cb74cd2 Latte: Small refactor and clean up for texture size code 2024-03-11 21:57:37 +01:00
Exzap
0993658c82 GX2: Rework GX2Set*UniformReg
- Use cafeExportRegister() instead of legacy export
- Submit as a single PM4 packet
- Add logging for the special case of the size parameter (not sure if this is used by any game?)
- Add some extra validation and logging which may be helpful to homebrew devs
2024-03-11 21:57:37 +01:00
Exzap
3d0d987d89 Logging: Introduce logOnce helper
For cases where printing a message once is enough and to avoid spamming log.txt
2024-03-11 21:57:37 +01:00
goeiecool9999
bb88b5c36d
Fix crash introduced by #1115 (#1117)
* Revert "CafeSystem: Init recompiler after game profile has been loaded (#1115)"
* Instead move gameprofile load call
2024-03-11 02:40:47 +01:00
goeiecool9999
ccabd93159
Linux: Exit on SIGTERM (#1116) 2024-03-11 02:13:53 +01:00
goeiecool9999
788da3cdf7
CafeSystem: Init recompiler after game profile has been loaded (#1115) 2024-03-11 01:47:31 +01:00
goeiecool9999
e1435066ee
OpenGL: Fix crash related to wxWidgets handling of vsync (#1112) 2024-03-11 00:57:31 +01:00
goeiecool9999
a2d74972d4
Prevent changing of console language while a game is running (#1114) 2024-03-11 00:55:31 +01:00
goeiecool9999
f69fddc6e5
TitleManager: Fix crash when sorting by format (#1113) 2024-03-10 23:25:16 +01:00
Exzap
d9e8ca2c83 Revert "Vulkan: Update some code to use VK_KHR_synchronization2"
This reverts commit 8f1cd4f925.

We received reports from users stuck with Vulkan drivers from 2019. (E.g. Kepler on Windows). So let's not unnecessarily increase the Vulkan requirement for now and postpone this to after the next stable release
2024-03-09 02:38:08 +01:00
Exzap
b390023bc5 README.md: Fix minor ambiguity 2024-03-08 14:48:59 +01:00
Exzap
ea68f787eb Vulkan: For MSAA surface copies make the target MSAA too
Fixes #1108
2024-03-08 14:45:02 +01:00
Exzap
9f9bc9865f Vulkan: Avoid calling vkCmdClearColorImage() on compressed textures
This is not allowed according to the spec and can crash drivers. Fixes #1100
2024-03-08 14:43:40 +01:00
Exzap
b8d81283e8 Vulkan: Remove unnecessary index buffer for backbuffer drawcall 2024-03-08 14:39:46 +01:00
Exzap
8f1cd4f925 Vulkan: Update some code to use VK_KHR_synchronization2 2024-03-08 14:39:46 +01:00
Simon
49c55a3f56
nsyshid: remove stray print statements (#1106) 2024-03-06 14:37:36 +01:00
Leif Liddy
8b37e316d0
BUILD.md: Add llvm package for Fedora (#1101) 2024-02-24 20:47:06 +01:00
Exzap
a63678c1f4 Update SDL2 vcpkg port to 2.30.0 2024-02-20 11:10:35 +01:00
Exzap
72ce4838ea Latte: Optimize uniform register array size for known shaders 2024-02-20 10:57:05 +01:00
Exzap
96bbd3bd25 Latte: Avoid assert in texture view check 2024-02-20 10:57:05 +01:00
MoonlightWave-12
3a02490a1f
BUILD.md: Mention Debian in the build-instructions for Ubuntu (#1096) 2024-02-18 17:12:09 +01:00
rawdatafeel
8d7fc98275
Improve BUILD.md (#1093) 2024-02-18 05:59:00 +01:00
capitalistspz
ed01eaf5f9
Gamelist: Add right-click actions for copying title ID, name, and icon (#1089) 2024-02-18 05:56:36 +01:00
Steveice10
9bbb7c8b97
Add support for portable directory without build flag (#1071) 2024-02-18 05:54:41 +01:00
Squall Leonhart
6a08d04af9
UI: Make Alt+F4/Ctrl+Q more reliable (#1035) 2024-02-18 05:52:11 +01:00
Squall Leonhart
81acd80a97
Cubeb: Add a default device to the selection (#1017) 2024-02-18 05:51:00 +01:00
Exzap
ca01e923bf Update issue templates 2024-01-20 00:33:39 +01:00
Exzap
4e4ac0de51 CI: For the Windows build use as many cores as available 2024-01-19 23:32:43 +01:00
capitalistspz
18679af4ec Ignore Wii U pro controller 2024-01-19 16:25:04 +01:00
Mike Lothian
72aacbdcec Vulkan: Don't use glslang internal headers
Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
2024-01-19 04:58:08 +01:00
Colin Kinloch
e53c63b828 Flatpak: Create shortcuts that launch flatpak 2024-01-17 02:49:58 +01:00
Colin Kinloch
f899ab7c34 Vulkan: Check for 0 size before wayland resize
Fixes "Launching games directly with the --title-id argument doesn't work
in Wayland" (#999)
2024-01-17 02:18:29 +01:00
Live session user
7e778042ee Fix macos missing dylib file 2024-01-16 14:51:12 +01:00
Exzap
f58b260cbd Fix macos missing dylib file 2024-01-15 16:32:04 +01:00
Exzap
f39a5e757b Add "Open MLC folder" option
Also updated Patron supporter list
2024-01-15 15:15:00 +01:00
Exzap
9b0a1d53dc Latte: Fix syntax error in generated GLSL 2024-01-14 23:40:29 +01:00
Exzap
223833cac4 Update libraries 2024-01-13 22:36:04 +01:00
GaryOderNichts
4405116324
GDBStub: Support watchpoints on linux (#1030)
* GDBStub: Support watchpoints on linux
* GDBStub: Use `TCP_NODELAY`
2023-12-23 15:25:01 -08:00
Exzap
bab1616565 nsysnet: Add support for SO_BIO and handle SO_ENOTCONN 2023-12-13 22:43:51 +01:00
Exzap
d2ba4e65c5 Latte: 1D views are compatible with 1D textures 2023-12-13 18:10:10 +01:00
Exzap
2167143c17 Latte: Support for SAMPLE_LB 2023-12-13 12:45:20 +01:00
Exzap
df282ab230 Latte: Clean up OpenGL relics in shared render code 2023-12-13 12:45:20 +01:00
Exzap
646835346c Latte: Refactor legacy OpenGL code for shader binding 2023-12-13 12:45:20 +01:00
Exzap
dee764473d Latte: Small refactor for GLSL texture coord handling
Also adds support for 2D textures coordinates with source as 0.0 or 1.0 literals instead of GPRs. Seen in shaders generated by CafeGLSL
2023-12-10 08:30:52 +01:00
Exzap
e7fa8ec0c6 Vulkan: Properly shut down compilation threads 2023-12-10 08:30:52 +01:00
Exzap
bffeb818d1 GfxPack: Refactor + better unicode support 2023-12-10 08:30:52 +01:00
Exzap
67f7ce815c nn_pdm: Refactor code to use new module structure 2023-12-10 08:30:52 +01:00
Exzap
9398c0ca6b Latte: Simplify and fix texture copy 2023-12-10 08:30:52 +01:00
shinra-electric
f6bb666abf
Mac: Add wua filetype to info.plist (#1039) 2023-12-10 08:30:08 +01:00
qurious-pixel
b6aaf66330
[AppImage] Bundle libstdc++ (#1038) 2023-12-07 02:07:50 +01:00
capitalistspz
1849083073
Use hidapi for Wiimotes on Windows (#1033) 2023-12-06 02:33:29 +01:00
shinra-electric
09409a5108
Set macOS min version to 12.0 Monterey (#1025) 2023-11-27 12:24:26 +01:00
GaryOderNichts
5047c4d083
GDBStub: Fix checkSum string to int conversion (#1029) 2023-11-27 12:21:52 +01:00
Exzap
f3c95f72e7 nn_fp: Multiple fixes 2023-10-19 05:55:52 +02:00
Exzap
b0a7fd4e07 Set default alignment for SysAllocator to cache-line size
Avoids memory corruptions when the memory is cleared via DCZeroRange. Seen in BotW with AX AUX buffers.
2023-10-18 10:49:59 +02:00
Exzap
9bb409314d coreinit: Fix potential race condition in IPC code 2023-10-18 10:43:36 +02:00
bslhq
9ec50b865d
Fix nfc menu list of recent nfc files (#996) 2023-10-17 14:45:55 +02:00
Exzap
63861bf812 Fix SpotPass downloads on Linux/MacOS 2023-10-17 13:07:43 +02:00
Exzap
66711529be Avoid wxGetKeyState since it asserts on Linux with wayland GTK
Only modifier keys are allowed, but we used it to test for Escape
2023-10-17 13:06:45 +02:00
Exzap
c440ecdf36 FPD: Fix a crash due to incorrect instantiation 2023-10-17 06:16:29 +02:00
Exzap
2959802ae2 Use utf-8 for exe path 2023-10-17 05:26:30 +02:00
Exzap
0d71885c88 nn_fp: Full rework of friend service 2023-10-17 05:26:30 +02:00
Francesco Saltori
13a50a915e
Fix several language selection issues (#994) 2023-10-16 13:41:06 +02:00
goeiecool9999
d4a2a8e8de
Vulkan: Cleanup image barrier code (#988) 2023-10-16 07:33:12 +02:00
Cemu-Language CI
db44a2d130 Update translation files 2023-10-04 21:39:01 +00:00
Exzap
db53f3b980 Fixes for titles in NUS format
Symlinks were not handled correctly
2023-10-02 21:24:50 +02:00
Exzap
29c823fa1f Latte: Fix uniform size limit being too low 2023-10-02 19:05:44 +02:00
Exzap
757d458161 Compatibility with fmtlib 10.1.x 2023-10-02 18:53:00 +02:00
Exzap
ff9d180154 Code cleanup 2023-10-01 11:46:26 +02:00
Exzap
9523993a24 Fix file menu list of recent games 2023-10-01 11:46:26 +02:00
Francesco Saltori
5b27d32cb7
Minor localization adjustments (#984) 2023-09-30 15:27:56 +02:00
Exzap
43976ca7eb Prioritize non-NUS format over NUS
If a title exists multiple times in the game folder in different formats, then prefer and use non-NUS format if one is available. This is so we match previous Cemu behavior where Cemu would pick non-NUS simply due the fact that NUS format wasn't supported yet.
2023-09-30 06:21:14 +02:00
Exzap
ce34b95b82 Fix game path not respecting utf8 encoding 2023-09-30 03:07:49 +02:00
Exzap
8bb7ce098c
Bump CI clang version to 15 + workaround for unsafe fiber optimizations (#982) 2023-09-29 17:17:28 +02:00
Exzap
8a4abb8bbb Update Windows build instructions 2023-09-29 06:48:10 +02:00
Exzap
6217276681 Enable DPI awareness on Windows 2023-09-29 06:48:10 +02:00
Exzap
21c1f84a87 Fix WUA conversion not detecting updates 2023-09-29 06:48:10 +02:00
Exzap
abce406ee8 Refactor more wstring instances to utf8-encoded string 2023-09-29 06:48:10 +02:00
Exzap
f6c3c96d94 More detailed error messages when encrypted titles fail to launch 2023-09-29 06:48:10 +02:00
Exzap
5ad57bb0c9 Add support for games in NUS format (.app)
Requires title.tmd and title.tik in same directory
2023-09-29 06:48:10 +02:00
Exzap
f9f6206929 Vulkan: Add profiler for Vulkan API CPU cost
Disabled by default. Set VULKAN_API_CPU_BENCHMARK to 1 to enable
2023-09-29 06:48:10 +02:00
Exzap
3e925b7707 Latte: Bound uniform buffers based on access patterns within the shader 2023-09-29 06:48:10 +02:00
Exzap
4d6b72b353 Latte: Very minor refactor + optimization 2023-09-29 06:48:10 +02:00
Leif Liddy
65e5e20afc
BUILD.md: Require libtool and libusb1-devel for Fedora (#979) 2023-09-27 00:29:23 +02:00
Squall Leonhart
638c4014a1
nn_olv: Handle nullptr key in SetSearchKey (#974) 2023-09-22 19:20:22 +02:00
goeiecool9999
b4aa10bee4
Vulkan: Only create imgui renderpass once (#972) 2023-09-20 19:01:56 +02:00
Joshua de Reeper
1d398551e2
Add DS_Store to gitignore (#969) 2023-09-19 21:43:54 +02:00
Exzap
90c56b7731
Latte: Optimizations and tweaks (#706) 2023-09-19 21:17:21 +02:00
capitalistspz
323bdfa183
More changes to finding wiimotes (#961) 2023-09-19 17:54:38 +02:00
Simon
98b5a8758a
nsyshid: Add backends for cross platform USB passthrough support (#950) 2023-09-19 01:27:40 +02:00
Exzap
2a735f1fb7 coreinit: Use native COS locks instead of STL 2023-09-14 20:52:40 +02:00
Exzap
14dd7a72a7 Add coding style guidelines and clang-format file 2023-09-14 20:52:40 +02:00
Exzap
92ab87b049 Latte: Fix shader compilation error when subroutines are used
Fixes character colors in Tekken Tag Tournament 2
2023-09-14 20:52:40 +02:00
Exzap
b902aa2048 Logging: Refactor and optimizations 2023-09-14 20:52:40 +02:00
Exzap
fda5ec2697 ih264d: Small optimizations and experiments with multi-threading
Using the multi-threaded decoder doesn't seem to be worth it but at least we have a way to enable it now
2023-09-14 20:52:40 +02:00
Exzap
f04c7575d7 coreinit: Handle non-existing modules in OSDynLoad_Acquire
Fixes Togabito crashing on boot

coreinit: Handle non-existing modules in OSDynLoad_Acquire
2023-09-14 20:52:40 +02:00
Exzap
c168cf536a Vulkan: Dont immediately crash on bad pipeline cache 2023-09-14 20:52:40 +02:00
Exzap
62889adfde Use memory barriers in Linux fiber implementation
Prevent compilers from caching TLS variables across swapcontext calls
2023-09-14 20:52:40 +02:00
Exzap
524188bb7a Refactor more GX2 code to use LatteReg.h 2023-09-14 20:52:40 +02:00
Francesco Saltori
96800c6f97
Additional localization fixes (#966) 2023-09-14 12:47:59 +02:00
Francesco Saltori
c66ab0c51a
Use native language names in language selector (#964) 2023-09-08 02:09:28 +02:00
Francesco Saltori
c16e258c93
Localization improvements and fixes (#956) 2023-09-08 02:09:03 +02:00
Cemu-Language CI
4d1864c8a1 Update translation files 2023-09-07 23:35:58 +00:00
Gloria
d7f0d67904
Add a pairing utility for Wiimotes to Cemu (#941) 2023-09-06 04:59:50 +02:00
jn64
2abf1c2059
Disable auto-update on Linux/macOS (#955)
It's not implemented yet
2023-09-02 05:57:21 +02:00
capitalistspz
5e84862e28
[Linux/MacOS] Further Wiimote changes for parity with Windows (#945) 2023-08-31 03:29:12 +02:00
GaryOderNichts
d8b9a74d86
Latte: rendertarget is a bitmask (#942) 2023-08-16 23:52:06 +02:00
capitalistspz
85aa4f095b
Linux/MacOS: Add wiimote support via HIDAPI (#934) 2023-08-15 09:37:37 +02:00
Exzap
892ae13680 Log Windows version + Fix logging crash on Linux 2023-08-13 14:50:38 +02:00
Colin Kinloch
890df997cb
Simplify appstream summary description (#932) 2023-08-08 23:23:18 +02:00
Colin Kinloch
22bf6420d2
Log platform info (#931) 2023-08-08 23:22:22 +02:00
Exzap
a17111e6b0 TitleManager: Improvements for .wua conversion
- Print more detailed paths in confirmation dialogue
- Prefer the title right clicked by the user
- When sourcing titles from other .wua files, use the correct subpath
Fix include path
2023-08-03 20:31:12 +02:00
Exzap
911573e0dd TitleList: Use narrower filter for identifying data titles
Previous code accidentally caught some game updates and dlc titles
2023-08-03 20:31:12 +02:00
Exzap
0f469eb2b9 Small cleanup + Fix memory base logged as 0 2023-08-03 20:31:12 +02:00
Exzap
6268a24a4b Fix crash in title manager 2023-08-03 20:31:12 +02:00
Exzap
0d96255bae nn_olv: More work on post API 2023-08-03 20:31:12 +02:00
Exzap
67819a68d9 nn_act: Handle incorrect slot 0 for PersistentId 2023-08-03 20:31:12 +02:00
Crementif
651e5336b4
debugger: Add logging breakpoint + misc fixes (#927) 2023-08-03 15:45:11 +02:00
Colin Kinloch
1d1e1e781b
Vulkan: Retry instance creation if validation layer is not present (#909) 2023-08-03 15:16:22 +02:00
capitalistspz
7111cbb103
Quote and escape desktop entry executable path (#917) 2023-08-03 14:54:16 +02:00
Alexandre Bouvier
34ff9da097
cmake: exclude fmt10 (#921) 2023-07-31 01:15:44 +02:00
Exzap
2200cc0ddf
Initial support for title switching + better Wii U menu compatibility (#907) 2023-07-21 13:54:07 +02:00
capitalistspz
bfbeeae6f6
Linux: Save icons to user data directory + use real path of AppImage for shortcuts (#894) 2023-07-10 00:53:00 +02:00
capitalistspz
f1c200a016
Add '--title-id' parameter and desktop shortcut creation (#889)
* Add '--title-id' launch option to launch titles by title id
* Add title id column to game list
* Add option to create game shortcuts

Co-authored-by: Exzap <13877693+Exzap@users.noreply.github.com>
2023-07-08 01:48:41 +02:00
Rambo6Glaz
ea86c77088
Fix UTC offset calculation (#890) 2023-07-05 20:20:01 +02:00
Rambo6Glaz
b0ae008a89
Fix NEX DataStore uploads (nlibcurl) and stub Miiverse PostApp (#888) 2023-07-05 18:59:33 +02:00
Exzap
9499870cc9 Debugging: Add minimalist PPC profiler 2023-06-25 08:40:52 +02:00
Exzap
45072fccb2 Remove remnants of frame profiler code 2023-06-25 08:40:52 +02:00
Exzap
4a04fab9c4 coreinit: Set file flag in stat 2023-06-25 08:40:52 +02:00
Rambo6Glaz
a8d157d310
nn_olv: Added community related API (#873)
- Initialize
- Download communities (self-made / favorites / officials)
- Upload communities (create subcommunity)
- Upload favorite status (Add/Delete favorite to a subcommunity)

Enough for support of Mario Kart 8 tournaments
2023-06-24 14:51:41 +02:00
Squall Leonhart
1beec40445
Fix logging related crash in KPADGetUnifiedWpadStatus (#875) 2023-06-22 04:11:31 +02:00
Valentin
cd3fe0685f
docs: Fix 404 link in issue template (#868) 2023-06-18 12:16:50 +02:00
Squall Leonhart
a533c485d9
Remove forced singlecore-recompiler from MK8 game profiles (#865) 2023-06-17 16:48:05 +02:00
Maschell
fd3b5b7f27
coreinit: Fix FSGetMountSource and FSAppendFile implementation (#863)
Fixes SD access in SSBU
2023-06-17 13:51:55 +02:00
Chris Purnell
950e9564e6
Fix register window not updating in PPC debugger (#864) 2023-06-17 13:50:40 +02:00
Chris Purnell
f0ef8f3cc2
Fix some out of bound array access. (#862) 2023-06-16 20:15:43 +02:00
Chris Purnell
f6a1cc598e
Fix error message when SDL fails to initialize (#861) 2023-06-16 19:57:27 +02:00
Exzap
633e5c0e05 Fix incorrect return values in OSAtomic 2023-06-15 22:36:45 +02:00
Exzap
808d1bb424 Add debug asserts for invalid MEMPTR
Also fixed some corruptions this uncovered
2023-06-15 22:36:35 +02:00
Exzap
7886b594a2 nsysnet: Implement inet_ntop 2023-06-15 22:36:25 +02:00
Exzap
0bead50065 coreinit: Implement support for ".." in paths 2023-06-15 21:23:18 +02:00
Maschell
f1ebfa9941
coreinit: Implement several FSA functions and fix some bugs (#844) 2023-06-15 21:05:16 +02:00
Colin Kinloch
ae4cb45cf3
Check wx dialogs aren't returning blank paths (#845) 2023-06-06 14:17:41 +02:00
Maschell
6073ab3ec6
coreinit: Refactor FS functions to be more accurate (#842) 2023-06-04 11:13:45 +02:00
Colin Kinloch
4ae5b4f8b8
Wayland: Use viewporter (#836) 2023-05-28 02:04:24 +02:00
Crementif
d903b2cf12
Remove deprecated logging system and expose developer logging window (#825) 2023-05-20 02:46:12 +02:00
Alexander
b8dec03cb5
Add missing template arguments for std::forward (#819) 2023-05-15 18:31:53 +02:00
Exzap
eda4cf4b24
Fix UI crash on Windows (#813) 2023-05-11 15:27:45 +02:00
Cemu-Language CI
dba724c4a0 Update translation files 2023-05-11 06:58:20 +00:00
Cemu-Language CI
5530e5c613 Revert "Update translation files"
This reverts commit 5c1c05adde.
2023-05-11 08:56:02 +02:00
Cemu-Language CI
5c1c05adde Update translation files 2023-05-11 06:35:06 +00:00
Nicholas F
1bcdb35e42
Add GameMode support for Linux build (#796) 2023-05-11 07:19:44 +02:00
Jonathan Barrow
b74ae21953
Resolve domains to IPs in Friends NEX (#807) 2023-05-10 11:51:54 +02:00
goeiecool9999
d56bc807cf
GraphicsPackWindow2: Use UTF8 (#802) 2023-04-26 15:41:23 +02:00
why-keith
1f16e999c5
Migrate missing forceLog_printf statement to new logging (#800) 2023-04-25 18:15:37 +02:00
why-keith
e58a84629f
Migrate coreinitMemLog_printf to new logging (#799) 2023-04-25 18:09:00 +02:00
why-keith
caa57a3cfd
Logging migration (forceLogDebug_printf) (#780)
* script changes - no arguments

* script changes with 2 arguments

* script changes with > 2 arguments

* script conversions with 1 argument - pt. 1

* script conversions with 1 argument - pt. 2

* script conversions with 1 argument - pt. 3

* script conversions with 1 argument - pt. 4

* script conversions with 1 argument - pt. 5

Pointer format hunting

* Fixed pointer format

* script conversions with 1 argument - final

* fixed conversion in non utf-8 file

* fixed conversion with capital letter

* actually fixed conversion with capital letter

* fixed another capital lettering issue

* Added conversions with LR removed

* removed LR from logs

* Converted logs that previously contained LR

* converted log that originally specified string length

* fixed log with commas in main text

* fixed multi-line log

* Fixed more logs with commas in main text

* Fixed unformatted pointer

* added conversion with float value

* converted lines with double parameters

* converted missed line

* corrected argument formatting

Co-authored-by: Crementif <26669564+Crementif@users.noreply.github.com>

* Fixed misspellings of "unhandled"

unhandeled -> unhandled

Co-authored-by: Crementif <26669564+Crementif@users.noreply.github.com>

---------

Co-authored-by: Crementif <26669564+Crementif@users.noreply.github.com>
2023-04-25 08:43:31 +02:00
Exzap
f48ad6a1ca Dont format logging calls with only the format string 2023-04-20 14:37:11 +02:00
586 changed files with 41903 additions and 17873 deletions

66
.clang-format Normal file
View file

@ -0,0 +1,66 @@
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLambdasOnASingleLine: Inline
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterExternBlock: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeElse: true
BeforeWhile: true
SplitEmptyFunction: false
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
ColumnLimit: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCSpaceAfterProperty: false
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceBeforeCtorInitializerColon: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Latest
TabWidth: 4
UseTab: Always

View file

@ -1,34 +0,0 @@
---
name: Bug Report / Feature Request
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Cemu or you are requesting a feature you believe would make Cemu better.
title: ''
labels: ''
assignees: ''
---
<!---
Please keep in mind Cemu is EXPERIMENTAL SOFTWARE.
Please read the FAQ:
https://cemu.info/faq.html/
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
https://discord.com/invite/5psYsup/
If the FAQ does not answer your question, please go to:
https://discord.com/invite/5psYsup/
When submitting an issue, please check the following:
- You have read the above.
- You have provided the version (commit hash) of Cemu you are using.
- You have provided sufficient detail for the issue to be reproduced.
- You have provided system specs (if relevant).
- Please also provide:
- For any issues, a log file
- For crashes, a backtrace.
- For graphical issues, comparison screenshots with real hardware.
- For emulation inaccuracies, a test-case (if able).
-->

View file

@ -2,4 +2,4 @@ blank_issues_enabled: false
contact_links:
- name: Cemu Discord
url: https://discord.com/invite/5psYsup
about: If you are experiencing an issue with Cemu, and you need tech support, or if you have a general question, try asking in the official Cemu Discord linked here. Piracy is not allowed.
about: If you need technical support with Cemu or have other questions the best place to ask is on the official Cemu Discord linked here

View file

@ -0,0 +1,69 @@
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Bug Report
description: Report an issue with Cemu emulator
title: "Enter a title for the bug report here"
labels: bug
body:
- type: markdown
id: md_readme
attributes:
value: |
## Important: Read First
If you discovered a bug you can report it here. Please make sure of the following first:
- That you are using the latest version of Cemu
- Only report something if you are sure it's a bug and not any technical issue on your end. For troubleshooting help see the [links page](https://github.com/cemu-project/Cemu#links)
- Problems specific to a single game should be reported on the [compatibility wiki](https://wiki.cemu.info/wiki/Main_Page) instead
- Verify that your problem isn't already mentioned on the [issue tracker](https://github.com/cemu-project/Cemu/issues)
Additionally, be aware that graphic packs can also causes issues. There is a separate issue tracker for graphic pack bugs over at the [graphic pack repository](https://github.com/cemu-project/cemu_graphic_packs)
- type: textarea
id: current_behavior
attributes:
label: Current Behavior
description: "What the bug is, in a brief description"
validations:
required: true
- type: textarea
id: expected_behavior
attributes:
label: Expected Behavior
description: "What did you expect to happen?"
validations:
required: true
- type: textarea
id: steps_to_reproduce
attributes:
label: Steps to Reproduce
description: "How to reproduce the issue"
validations:
required: true
- type: textarea
id: sys_info
attributes:
label: System Info (Optional)
description: "Your PC specifications. Usually only the operating system and graphics card is important. But feel free to add more info."
placeholder: |
Info
OS: Windows 10
GPU: NVIDIA GeForce RTX 4090
value: |
OS:
GPU:
- type: textarea
id: emulation_settings
attributes:
label: Emulation Settings (Optional)
description: |
Any non-default settings. You can leave this empty if you didn't change anything other than input settings.
validations:
required: false
- type: textarea
id: logs_files
attributes:
label: "Logs (Optional)"
description: |
"Attach `log.txt` from your Cemu folder (*File > Open Cemu folder*)".
validations:
required: false

View file

@ -0,0 +1,28 @@
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Feature suggestion
description: Suggest a new feature
title: "Enter a title for the suggestion here"
labels: feature request
body:
- type: markdown
id: md_readme
attributes:
value: |
## Important: Read First
While we appreciate suggestions, it is important to note that we are a very small team and there are already many more ideas than we could ever implement in the near future. Therefore, please only suggest something if you believe it is a great addition and the idea is reasonably unique.
*Avoid* to create suggestions for:
- Overly obvious features ("Game xyz does not work and should be fixed", "Wiimote support should be improved", "You should add an Android port", "Copy feature xyz from another emulator", "A button to pause/stop emulation")
- Niche features which are only interesting to a tiny percentage of users
- Large scale features ("Add a Metal backend for MacOS", "Add ARM support", "Add savestates")
Note that this doesn't mean we aren't interested in these ideas, but rather we likely have them planned anyway and it's mostly up to finding the time to implement them.
If you believe your idea is worthwhile even if it doesn't meet all the criteria above, you can still try suggesting it but we might close it.
- type: textarea
id: idea_suggestion
attributes:
label: Your suggestion
description: "Describe what your suggestion is in as much detail as possible"
validations:
required: true

View file

@ -1,9 +0,0 @@
#include <stdio.h>
#include "./../src/Common/version.h"
// output current Cemu version for CI workflow. Do not modify
int main()
{
printf("%d.%d", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR);
return 0;
}

View file

@ -3,10 +3,10 @@ name: Build Cemu
on:
workflow_call:
inputs:
deploymode:
next_version_major:
required: false
type: string
experimentalversion:
next_version_minor:
required: false
type: string
@ -16,44 +16,35 @@ env:
jobs:
build-ubuntu:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: "Checkout repo"
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: "recursive"
fetch-depth: 0
- name: "Fetch full history for vcpkg submodule"
run: |
cd dependencies/vcpkg
git fetch --unshallow
git pull --all
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}
- name: Setup release mode parameters
run: |
echo "BUILD_MODE=release" >> $GITHUB_ENV
echo "BUILD_FLAGS=" >> $GITHUB_ENV
echo "Build mode is release"
- name: Setup debug mode parameters (for continous build)
if: ${{ inputs.deploymode != 'release' }}
- name: Setup build flags for version
if: ${{ inputs.next_version_major != '' }}
run: |
echo "BUILD_MODE=debug" >> $GITHUB_ENV
echo "BUILD_FLAGS=" >> $GITHUB_ENV
echo "Build mode is debug"
- name: Setup version for experimental
if: ${{ inputs.experimentalversion != '' }}
run: |
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" >> $GITHUB_ENV
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV
- name: "Install system dependencies"
run: |
sudo apt update -qq
sudo apt install -y clang-12 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libudev-dev nasm ninja-build
sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libudev-dev nasm ninja-build libbluetooth-dev
- name: "Setup cmake"
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.29.0'
- name: "Bootstrap vcpkg"
run: |
@ -75,31 +66,29 @@ jobs:
- name: "cmake"
run: |
cmake -S . -B build ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DPORTABLE=OFF -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja
cmake -S . -B build ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja
- name: "Build Cemu"
run: |
cmake --build build
- name: Prepare artifact
if: ${{ inputs.deploymode == 'release' }}
run: mv bin/Cemu_release bin/Cemu
- name: Upload artifact
uses: actions/upload-artifact@v3
if: ${{ inputs.deploymode == 'release' }}
uses: actions/upload-artifact@v4
with:
name: cemu-bin-linux-x64
path: ./bin/Cemu
build-appimage:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
needs: build-ubuntu
steps:
- name: Checkout Upstream Repo
uses: actions/checkout@v3
uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: cemu-bin-linux-x64
path: bin
@ -107,7 +96,7 @@ jobs:
- name: "Install system dependencies"
run: |
sudo apt update -qq
sudo apt install -y clang-12 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build appstream
sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build appstream libbluetooth-dev
- name: "Build AppImage"
run: |
@ -116,7 +105,7 @@ jobs:
dist/linux/appimage.sh
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: cemu-appimage-x64
path: artifacts
@ -125,34 +114,26 @@ jobs:
runs-on: windows-2022
steps:
- name: "Checkout repo"
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: "recursive"
- name: "Fetch full history for vcpkg submodule"
run: |
cd dependencies/vcpkg
git fetch --unshallow
git pull --all
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}
- name: Setup release mode parameters
run: |
echo "BUILD_MODE=release" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "Build mode is release"
- name: Setup debug mode parameters (for continous build)
if: ${{ inputs.deploymode != 'release' }}
- name: Setup build flags for version
if: ${{ inputs.next_version_major != '' }}
run: |
echo "BUILD_MODE=debug" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "Build mode is debug"
- name: Setup version for experimental
if: ${{ inputs.experimentalversion != '' }}
run: |
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
- name: "Setup cmake"
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.29.0'
- name: "Bootstrap vcpkg"
run: |
@ -183,56 +164,53 @@ jobs:
- name: "Build Cemu"
run: |
cd build
cmake --build . --config ${{ env.BUILD_MODE }} -j 2
cmake --build . --config ${{ env.BUILD_MODE }}
- name: Prepare artifact
if: ${{ inputs.deploymode == 'release' }}
run: Rename-Item bin/Cemu_release.exe Cemu.exe
- name: Upload artifact
uses: actions/upload-artifact@v3
if: ${{ inputs.deploymode == 'release' }}
uses: actions/upload-artifact@v4
with:
name: cemu-bin-windows-x64
path: ./bin/Cemu.exe
build-macos:
runs-on: macos-12
runs-on: macos-14
steps:
- name: "Checkout repo"
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: "recursive"
- name: "Fetch full history for vcpkg submodule"
run: |
cd dependencies/vcpkg
git fetch --unshallow
git pull --all
- name: Setup release mode parameters (for deploy)
if: ${{ inputs.deploymode == 'release' }}
- name: Setup release mode parameters
run: |
echo "BUILD_MODE=release" >> $GITHUB_ENV
echo "BUILD_FLAGS=" >> $GITHUB_ENV
echo "Build mode is release"
- name: Setup debug mode parameters (for continous build)
if: ${{ inputs.deploymode != 'release' }}
- name: Setup build flags for version
if: ${{ inputs.next_version_major != '' }}
run: |
echo "BUILD_MODE=debug" >> $GITHUB_ENV
echo "BUILD_FLAGS=" >> $GITHUB_ENV
echo "Build mode is debug"
- name: Setup version for experimental
if: ${{ inputs.experimentalversion != '' }}
run: |
echo "[INFO] Experimental version ${{ inputs.experimentalversion }}"
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEXPERIMENTAL_VERSION=${{ inputs.experimentalversion }}" >> $GITHUB_ENV
echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}"
echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV
- name: "Install system dependencies"
run: |
brew update
brew install llvm@15 ninja nasm molten-vk
brew install ninja nasm automake libtool
- name: "Install molten-vk"
run: |
curl -L -O https://github.com/KhronosGroup/MoltenVK/releases/download/v1.2.9/MoltenVK-macos.tar
tar xf MoltenVK-macos.tar
sudo mkdir -p /usr/local/lib
sudo cp MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib /usr/local/lib
- name: "Setup cmake"
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.29.0'
- name: "Bootstrap vcpkg"
run: |
@ -258,10 +236,8 @@ jobs:
cd build
cmake .. ${{ env.BUILD_FLAGS }} \
-DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
-DPORTABLE=OFF \
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
-DMACOS_BUNDLE=ON \
-DCMAKE_C_COMPILER=/usr/local/opt/llvm@15/bin/clang \
-DCMAKE_CXX_COMPILER=/usr/local/opt/llvm@15/bin/clang++ \
-G Ninja
- name: "Build Cemu"
@ -269,21 +245,19 @@ jobs:
cmake --build build
- name: Prepare artifact
if: ${{ inputs.deploymode == 'release' }}
run: |
mkdir bin/Cemu_app
mv bin/Cemu_release.app bin/Cemu_app/Cemu.app
mv bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu_release bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu
sed -i '' 's/Cemu_release/Cemu/g' bin/Cemu_app/Cemu.app/Contents/Info.plist
chmod a+x bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu
chmod a+x bin/Cemu_app/Cemu.app/Contents/MacOS/{Cemu,update.sh}
ln -s /Applications bin/Cemu_app/Applications
hdiutil create ./bin/tmp.dmg -ov -volname "Cemu" -fs HFS+ -srcfolder "./bin/Cemu_app"
hdiutil convert ./bin/tmp.dmg -format UDZO -o bin/Cemu.dmg
rm bin/tmp.dmg
- name: Upload artifact
uses: actions/upload-artifact@v3
if: ${{ inputs.deploymode == 'release' }}
uses: actions/upload-artifact@v4
with:
name: cemu-bin-macos-x64
path: ./bin/Cemu.dmg

View file

@ -16,6 +16,3 @@ on:
jobs:
build:
uses: ./.github/workflows/build.yml
with:
deploymode: release
experimentalversion: 999999

View file

@ -1,86 +0,0 @@
name: Deploy experimental release
on:
workflow_dispatch:
jobs:
call-release-build:
uses: ./.github/workflows/build.yml
with:
deploymode: release
experimentalversion: ${{ github.run_number }}
deploy:
name: Deploy experimental release
runs-on: ubuntu-20.04
needs: call-release-build
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: cemu-bin-linux-x64
path: cemu-bin-linux-x64
- uses: actions/download-artifact@v3
with:
name: cemu-appimage-x64
path: cemu-appimage-x64
- uses: actions/download-artifact@v3
with:
name: cemu-bin-windows-x64
path: cemu-bin-windows-x64
- uses: actions/download-artifact@v3
with:
name: cemu-bin-macos-x64
path: cemu-bin-macos-x64
- name: Initialize
run: |
mkdir upload
sudo apt install zip
- name: Get version
run: |
echo "Experimental version: ${{ github.run_number }}"
ls
gcc -o getversion .github/getversion.cpp
./getversion
echo "Cemu CI version: $(./getversion)"
echo "CEMU_FOLDER_NAME=Cemu_$(./getversion)-${{ github.run_number }}" >> $GITHUB_ENV
echo "CEMU_VERSION=$(./getversion)-${{ github.run_number }}" >> $GITHUB_ENV
- name: Create release from windows-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-windows-x64/Cemu.exe ./${{ env.CEMU_FOLDER_NAME }}/Cemu.exe
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-windows-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create appimage
run: |
VERSION=${{ env.CEMU_VERSION }}
echo "Cemu Version is $VERSION"
ls cemu-appimage-x64
mv cemu-appimage-x64/Cemu-*-x86_64.AppImage upload/Cemu-$VERSION-x86_64.AppImage
- name: Create release from linux-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-20.04-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create release from macos-bin
run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg
- name: Create release
run: |
wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.15.0/ghr_v0.15.0_linux_amd64.tar.gz
tar xvzf ghr.tar.gz; rm ghr.tar.gz
echo "[INFO] Release tag: v${{ env.CEMU_VERSION }}"
ghr_v0.15.0_linux_amd64/ghr -prerelease -t ${{ secrets.GITHUB_TOKEN }} -n "Cemu ${{ env.CEMU_VERSION }} (Experimental)" -b "Cemu experimental release" "v${{ env.CEMU_VERSION }}" ./upload

151
.github/workflows/deploy_release.yml vendored Normal file
View file

@ -0,0 +1,151 @@
name: Deploy release
on:
workflow_dispatch:
inputs:
changelog0:
description: 'Enter the changelog lines for this release. Each line is a feature / bullet point. Do not use dash.'
required: true
type: string
changelog1:
description: 'Feature 2'
required: false
type: string
changelog2:
description: 'Feature 3'
required: false
type: string
changelog3:
description: 'Feature 4'
required: false
type: string
changelog4:
description: 'Feature 5'
required: false
type: string
changelog5:
description: 'Feature 6'
required: false
type: string
changelog6:
description: 'Feature 7'
required: false
type: string
changelog7:
description: 'Feature 8'
required: false
type: string
changelog8:
description: 'Feature 9'
required: false
type: string
changelog9:
description: 'Feature 10'
required: false
type: string
jobs:
calculate-version:
name: Calculate Version
uses: ./.github/workflows/determine_release_version.yml
call-release-build:
uses: ./.github/workflows/build.yml
needs: calculate-version
with:
next_version_major: ${{ needs.calculate-version.outputs.next_version_major }}
next_version_minor: ${{ needs.calculate-version.outputs.next_version_minor }}
deploy:
name: Deploy release
runs-on: ubuntu-22.04
needs: [call-release-build, calculate-version]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate changelog
id: generate_changelog
run: |
CHANGELOG=""
if [ -n "${{ github.event.inputs.changelog0 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog0 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog1 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog1 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog2 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog2 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog3 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog3 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog4 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog4 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog5 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog5 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog6 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog6 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog7 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog7 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog8 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog8 }}\n"; fi
if [ -n "${{ github.event.inputs.changelog9 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog9 }}\n"; fi
echo -e "$CHANGELOG"
echo "RELEASE_BODY=$CHANGELOG" >> $GITHUB_ENV
- uses: actions/download-artifact@v4
with:
name: cemu-bin-linux-x64
path: cemu-bin-linux-x64
- uses: actions/download-artifact@v4
with:
name: cemu-appimage-x64
path: cemu-appimage-x64
- uses: actions/download-artifact@v4
with:
name: cemu-bin-windows-x64
path: cemu-bin-windows-x64
- uses: actions/download-artifact@v4
with:
name: cemu-bin-macos-x64
path: cemu-bin-macos-x64
- name: Initialize
run: |
mkdir upload
sudo apt install zip
- name: Set version dependent vars
run: |
echo "Version: ${{ needs.calculate-version.outputs.next_version }}"
echo "CEMU_FOLDER_NAME=Cemu_${{ needs.calculate-version.outputs.next_version }}"
echo "CEMU_VERSION=${{ needs.calculate-version.outputs.next_version }}"
echo "CEMU_FOLDER_NAME=Cemu_${{ needs.calculate-version.outputs.next_version }}" >> $GITHUB_ENV
echo "CEMU_VERSION=${{ needs.calculate-version.outputs.next_version }}" >> $GITHUB_ENV
- name: Create release from windows-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-windows-x64/Cemu.exe ./${{ env.CEMU_FOLDER_NAME }}/Cemu.exe
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-windows-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create appimage
run: |
VERSION=${{ env.CEMU_VERSION }}
echo "Cemu Version is $VERSION"
ls cemu-appimage-x64
mv cemu-appimage-x64/Cemu-*-x86_64.AppImage upload/Cemu-$VERSION-x86_64.AppImage
- name: Create release from linux-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-22.04-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create release from macos-bin
run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg
- name: Create release
run: |
wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.15.0/ghr_v0.15.0_linux_amd64.tar.gz
tar xvzf ghr.tar.gz; rm ghr.tar.gz
echo "[INFO] Release tag: v${{ env.CEMU_VERSION }}"
CHANGELOG_UNESCAPED=$(printf "%s\n" "${{ env.RELEASE_BODY }}" | sed 's/\\n/\n/g')
RELEASE_BODY=$(printf "%s\n%s" \
"**Changelog:**" \
"$CHANGELOG_UNESCAPED")
ghr_v0.15.0_linux_amd64/ghr -draft -t ${{ secrets.GITHUB_TOKEN }} -n "Cemu ${{ env.CEMU_VERSION }}" -b "$RELEASE_BODY" "v${{ env.CEMU_VERSION }}" ./upload

View file

@ -1,85 +0,0 @@
name: Create new release
on:
workflow_dispatch:
inputs:
PlaceholderInput:
description: PlaceholderInput
required: false
jobs:
call-release-build:
uses: ./.github/workflows/build.yml
with:
deploymode: release
deploy:
name: Deploy release
runs-on: ubuntu-20.04
needs: call-release-build
steps:
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: cemu-bin-linux-x64
path: cemu-bin-linux-x64
- uses: actions/download-artifact@v3
with:
name: cemu-appimage-x64
path: cemu-appimage-x64
- uses: actions/download-artifact@v3
with:
name: cemu-bin-windows-x64
path: cemu-bin-windows-x64
- uses: actions/download-artifact@v3
with:
name: cemu-bin-macos-x64
path: cemu-bin-macos-x64
- name: Initialize
run: |
mkdir upload
sudo apt update -qq
sudo apt install -y zip
- name: Get Cemu release version
run: |
gcc -o getversion .github/getversion.cpp
echo "Cemu CI version: $(./getversion)"
echo "CEMU_FOLDER_NAME=Cemu_$(./getversion)" >> $GITHUB_ENV
echo "CEMU_VERSION=$(./getversion)" >> $GITHUB_ENV
- name: Create release from windows-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-windows-x64/Cemu.exe ./${{ env.CEMU_FOLDER_NAME }}/Cemu.exe
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-windows-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create appimage
run: |
VERSION=${{ env.CEMU_VERSION }}
echo "Cemu Version is $VERSION"
ls cemu-appimage-x64
mv cemu-appimage-x64/Cemu-*-x86_64.AppImage upload/Cemu-$VERSION-x86_64.AppImage
- name: Create release from ubuntu-bin
run: |
ls ./
ls ./bin/
cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }}
mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu
zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-20.04-x64.zip ${{ env.CEMU_FOLDER_NAME }}
rm -r ./${{ env.CEMU_FOLDER_NAME }}
- name: Create release from macos-bin
run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg
- name: Create release
run: |
wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.15.0/ghr_v0.15.0_linux_amd64.tar.gz
tar xvzf ghr.tar.gz; rm ghr.tar.gz
ghr_v0.15.0_linux_amd64/ghr -t ${{ secrets.GITHUB_TOKEN }} -n "Cemu ${{ env.CEMU_VERSION }}" -b "Changelog:" v${{ env.CEMU_VERSION }} ./upload

View file

@ -0,0 +1,74 @@
name: Calculate Next Version from release history
on:
workflow_dispatch:
workflow_call:
outputs:
next_version:
description: "The next semantic version"
value: ${{ jobs.calculate-version.outputs.next_version }}
next_version_major:
description: "The next semantic version (major)"
value: ${{ jobs.calculate-version.outputs.next_version_major }}
next_version_minor:
description: "The next semantic version (minor)"
value: ${{ jobs.calculate-version.outputs.next_version_minor }}
jobs:
calculate-version:
runs-on: ubuntu-latest
outputs:
next_version: ${{ steps.calculate_next_version.outputs.next_version }}
next_version_major: ${{ steps.calculate_next_version.outputs.next_version_major }}
next_version_minor: ${{ steps.calculate_next_version.outputs.next_version_minor }}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Get all releases
id: get_all_releases
run: |
# Fetch all releases and check for API errors
RESPONSE=$(curl -s -o response.json -w "%{http_code}" "https://api.github.com/repos/${{ github.repository }}/releases?per_page=100")
if [ "$RESPONSE" -ne 200 ]; then
echo "Failed to fetch releases. HTTP status: $RESPONSE"
cat response.json
exit 1
fi
# Extract and sort tags
ALL_TAGS=$(jq -r '.[].tag_name' response.json | grep -E '^v[0-9]+\.[0-9]+(-[0-9]+)?$' | sed 's/-.*//' | sort -V | tail -n 1)
# Exit if no tags were found
if [ -z "$ALL_TAGS" ]; then
echo "No valid tags found."
exit 1
fi
echo "::set-output name=tag::$ALL_TAGS"
# echo "tag=$ALL_TAGS" >> $GITHUB_STATE
- name: Calculate next semver minor
id: calculate_next_version
run: |
LATEST_VERSION=${{ steps.get_all_releases.outputs.tag }}
# strip 'v' prefix and split into major.minor
LATEST_VERSION=${LATEST_VERSION//v/}
IFS='.' read -r -a VERSION_PARTS <<< "$LATEST_VERSION"
MAJOR=${VERSION_PARTS[0]}
MINOR=${VERSION_PARTS[1]}
# increment the minor version
MINOR=$((MINOR + 1))
NEXT_VERSION="${MAJOR}.${MINOR}"
echo "Major: $MAJOR"
echo "Minor: $MINOR"
echo "Next version: $NEXT_VERSION"
echo "::set-output name=next_version::$NEXT_VERSION"
echo "::set-output name=next_version_major::$MAJOR"
echo "::set-output name=next_version_minor::$MINOR"

View file

@ -29,13 +29,13 @@ jobs:
- name: "Generate POT file using xgettext"
run: >
find src -name *.cpp -o -name *.hpp -o -name *.h |
xargs xgettext --from-code=utf-8
-k_ -kwxTRANSLATE -w 100
xargs xgettext --from-code=utf-8 -w 100
--keyword="_" --keyword="wxTRANSLATE" --keyword="wxPLURAL:1,2"
--check=space-ellipsis --omit-header
-o cemu.pot
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: POT file
path: ./cemu.pot

8
.gitignore vendored
View file

@ -17,7 +17,7 @@
.idea/
build/
cmake-build-*-*/
cmake-build-*/
out/
.cache/
bin/Cemu_*
@ -38,6 +38,9 @@ bin/debugger/*
bin/sdcard/*
bin/screenshots/*
bin/dump/*
bin/cafeLibs/*
bin/portable/*
bin/keys.txt
!bin/shaderCache/info.txt
bin/shaderCache/*
@ -48,3 +51,6 @@ bin/controllerProfiles/*
bin/gameProfiles/*
bin/graphicPacks/*
# Ignore Finder view option files created by OS X
.DS_Store

4
.gitmodules vendored
View file

@ -9,10 +9,12 @@
[submodule "dependencies/vcpkg"]
path = dependencies/vcpkg
url = https://github.com/microsoft/vcpkg
shallow = true
shallow = false
[submodule "dependencies/Vulkan-Headers"]
path = dependencies/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers
shallow = true
[submodule "dependencies/imgui"]
path = dependencies/imgui
url = https://github.com/ocornut/imgui
shallow = true

250
BUILD.md
View file

@ -1,97 +1,184 @@
# Build instructions
# Build Instructions
## Table of Contents
- [Windows](#windows)
- [Linux](#linux)
- [Dependencies](#dependencies)
- [For Arch and derivatives:](#for-arch-and-derivatives)
- [For Debian, Ubuntu and derivatives](#for-debian-ubuntu-and-derivatives)
- [For Fedora and derivatives:](#for-fedora-and-derivatives)
- [Build Cemu](#build-cemu)
- [CMake and Clang](#cmake-and-clang)
- [GCC](#gcc)
- [Debug Build](#debug-build)
- [Troubleshooting Steps](#troubleshooting-steps)
- [Compiling Errors](#compiling-errors)
- [Building Errors](#building-errors)
- [macOS](#macos)
- [Installing brew](#installing-brew)
- [Installing Tool Dependencies](#installing-tool-dependencies)
- [Installing Library Dependencies](#installing-library-dependencies)
- [Build Cemu using CMake](#build-cemu-using-cmake)
- [Updating Cemu and source code](#updating-cemu-and-source-code)
## Windows
Prerequisites:
- A recent version of Visual Studio 2022 (recommended but not required) with the following additional components:
- C++ CMake tools for Windows
- Windows 10/11 SDK
- git
- A recent version of Visual Studio 2022 with the following additional components:
- C++ CMake tools for Windows
- Windows 10/11 SDK
Instructions:
Instructions for Visual Studio 2022:
1. Run `git clone --recursive https://github.com/cemu-project/Cemu`
2. Launch `Cemu/generate_vs_solution.bat`.
- If you installed VS to a custom location or use VS 2019, you may need to manually change the path inside the .bat file.
3. Wait until it's done, then open `Cemu/build/Cemu.sln` in Visual Studio.
4. Then build the solution and once finished you can run and debug it, or build it and check the /bin folder for the final Cemu_release.exe.
2. Open the newly created Cemu directory in Visual Studio using the "Open a local folder" option
3. In the menu select Project -> Configure CMake. Wait until it is done, this may take a long time
4. You can now build, run and debug Cemu
You can also skip steps 3-5 and open the root folder of the cloned repo directly in Visual Studio (as a folder) and use the built-in CMake support but be warned that cmake support in VS can be a bit finicky.
Any other IDE should also work as long as it has CMake and MSVC support. CLion and Visual Studio Code have been confirmed to work.
## Linux
To compile Cemu, a recent enough compiler and STL with C++20 support is required! clang-12 or higher is what we recommend.
To compile Cemu, a recent enough compiler and STL with C++20 support is required! Clang-15 or higher is what we recommend.
### Installing dependencies
#### For Ubuntu and derivatives:
`sudo apt install -y cmake curl freeglut3-dev git libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build`
*Additionally, for Ubuntu 22.04 only:*
- `sudo apt install -y clang-12`
- At step 3 while building, use
`cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja`
### Dependencies
#### For Arch and derivatives:
`sudo pacman -S --needed base-devel clang cmake freeglut git glm gtk3 libgcrypt libpulse libsecret linux-headers llvm nasm ninja systemd unzip zip`
`sudo pacman -S --needed base-devel bluez-libs clang cmake freeglut git glm gtk3 libgcrypt libpulse libsecret linux-headers llvm nasm ninja systemd unzip zip`
#### For Debian, Ubuntu and derivatives:
`sudo apt install -y cmake curl clang-15 freeglut3-dev git libbluetooth-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libtool nasm ninja-build`
You may also need to install `libusb-1.0-0-dev` as a workaround for an issue with the vcpkg hidapi package.
At Step 3 in [Build Cemu using cmake and clang](#build-cemu-using-cmake-and-clang), use the following command instead:
`cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja`
#### For Fedora and derivatives:
`sudo dnf install clang cmake cubeb-devel freeglut-devel git glm-devel gtk3-devel kernel-headers libgcrypt-devel libsecret-devel nasm ninja-build perl-core systemd-devel zlib-devel`
`sudo dnf install bluez-libs-devel clang cmake cubeb-devel freeglut-devel git glm-devel gtk3-devel kernel-headers libgcrypt-devel libsecret-devel libtool libusb1-devel llvm nasm ninja-build perl-core systemd-devel wayland-protocols-devel zlib-devel zlib-static`
### Build Cemu using cmake and clang
1. `git clone --recursive https://github.com/cemu-project/Cemu`
2. `cd Cemu`
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -G Ninja`
4. `cmake --build build`
5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`.
### Build Cemu
#### Using GCC
While we use and test Cemu using clang, using GCC might work better with your distro (they should be fairly similar performance/issues wise and should only be considered if compilation is the issue).
You can use GCC by doing the following:
- make sure you have g++ installed in your system
- installation for Ubuntu and derivatives: `sudo apt install g++`
- installation for Fedora and derivatives: `sudo dnf install gcc-c++`
- replace the step 3 with the following:
`cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ -G Ninja`
#### CMake and Clang
#### Troubleshooting steps
- If step 3 gives you an error about not being able to find ninja, try appending `-DCMAKE_MAKE_PROGRAM=/usr/bin/ninja` to the command and running it again.
- If step 3 fails while compiling the boost-build dependency, it means you don't have a working/good standard library installation. Check the integrity of your system headers and making sure that C++ related packages are installed and intact.
- If step 3 gives a random error, read the `[package-name-and-platform]-out.log` and `[package-name-and-platform]-err.log` for the actual reason to see if you might be lacking the headers from a dependency.
- If step 3 is still failing or if you're not able to find the cause, please make an issue on our Github about it!
- If step 3 fails during rebuild after `git pull` with an error that mentions RPATH, add this to the end of step 3: `-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON`
- If step 4 gives you an error that contains something like `main.cpp.o: in function 'std::__cxx11::basic_string...`, you likely are experiencing a clang-14 issue. This can only be fixed by either lowering the clang version or using GCC, see below.
- If step 4 gives you a different error, you could report it to this repo or try using GCC. Just make sure your standard library and compilers are updated since Cemu uses a lot of modern features!
- If step 4 gives you undefined libdecor_xx, you are likely experiencing an issue with sdl2 package that comes with vcpkg. Delete sdl2 from vcpkg.json in source file and recompile.
- If step 4 gives you `fatal error: 'span' file not found`, then you're either missing `libstdc++` or are using a version that's too old. Install at least v10 with your package manager, eg `sudo apt install libstdc++-10-dev`. See #644.
```
git clone --recursive https://github.com/cemu-project/Cemu
cd Cemu
cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -G Ninja
cmake --build build
```
#### GCC
If you are building using GCC, make sure you have g++ installed:
- Installation for Arch and derivatives: `sudo pacman -S gcc`
- Installation for Debian, Ubuntu and derivatives: `sudo apt install g++`
- Installation for Fedora and derivatives: `sudo dnf install gcc-c++`
```
git clone --recursive https://github.com/cemu-project/Cemu
cd Cemu
cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ -G Ninja
cmake --build build
```
#### Debug Build
```
git clone --recursive https://github.com/cemu-project/Cemu
cd Cemu
cmake -S . -B build -DCMAKE_BUILD_TYPE=debug -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -G Ninja
cmake --build build
```
If you are using GCC, replace `cmake -S . -B build -DCMAKE_BUILD_TYPE=debug -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -G Ninja` with `cmake -S . -B build -DCMAKE_BUILD_TYPE=debug -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ -G Ninja`
#### Troubleshooting Steps
##### Compiling Errors
This section refers to running `cmake -S...` (truncated).
* `vcpkg install failed`
* Run the following in the root directory and try running the command again (don't forget to change directories afterwards):
* `cd dependencies/vcpkg && git fetch --unshallow`
* `Please ensure you're using the latest port files with git pull and vcpkg update.`
* Either:
* Update vcpkg by running by the following command:
* `git submodule update --remote dependencies/vcpkg`
* If you are sure vcpkg is up to date, check the following logs:
* `Cemu/dependencies/vcpkg/buildtrees/wxwidgets/config-x64-linux-out.log`
* `Cemu/dependencies/vcpkg/buildtrees/libsystemd/config-x64-linux-dbg-meson-log.txt.log`
* `Cemu/dependencies/vcpkg/buildtrees/libsystemd/config-x64-linux-dbg-out.log`
* Not able to find Ninja.
* Add the following and try running the command again:
* `-DCMAKE_MAKE_PROGRAM=/usr/bin/ninja`
* Compiling failed during the boost-build dependency.
* It means you don't have a working/good standard library installation. Check the integrity of your system headers and making sure that C++ related packages are installed and intact.
* Compiling failed during rebuild after `git pull` with an error that mentions RPATH
* Add the following and try running the command again:
* `-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON`
* Environment variable `VCPKG_FORCE_SYSTEM_BINARIES` must be set.
* Execute the folowing and then try running the command again:
* `export VCPKG_FORCE_SYSTEM_BINARIES=1`
* If you are getting a random error, read the [package-name-and-platform]-out.log and [package-name-and-platform]-err.log for the actual reason to see if you might be lacking the headers from a dependency.
If you are getting a different error than any of the errors listed above, you may either open an issue in this repo or try using [GCC](#gcc). Make sure your standard library and compilers are updated since Cemu uses a lot of modern features!
##### Building Errors
This section refers to running `cmake --build build`.
* `main.cpp.o: in function 'std::__cxx11::basic_string...`
* You likely are experiencing a clang-14 issue. This can only be fixed by either lowering the clang version or using GCC, see [GCC](#gcc).
* `fatal error: 'span' file not found`
* You're either missing `libstdc++` or are using a version that's too old. Install at least v10 with your package manager, eg `sudo apt install libstdc++-10-dev`. See [#644](https://github.com/cemu-project/Cemu/issues/644).
* `undefined libdecor_xx`
* You are likely experiencing an issue with sdl2 package that comes with vcpkg. Delete sdl2 from vcpkg.json in source file and recompile.
If you are getting a different error than any of the errors listed above, you may either open an issue in this repo or try using [GCC](#gcc). Make sure your standard library and compilers are updated since Cemu uses a lot of modern features!
## macOS
To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and
below, built in LLVM, and Xcode LLVM don't support the C++20 feature set required. The OpenGL graphics
API isn't support on macOS, Vulkan must be used. Additionally Vulkan must be used through the
Molten-VK compatibility layer
### On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used
You can skip this section if you have an Intel Mac. Every time you compile, you need to perform steps 2.
1. `softwareupdate --install-rosetta` # Install Rosetta 2 if you don't have it. This only has to be done once
2. `arch -x86_64 zsh` # run an x64 shell
To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and below
don't support the C++20 feature set required, so either install LLVM from Homebrew or make sure that
you have a recent enough version of Xcode. Xcode 15 is known to work. The OpenGL graphics API isn't
supported on macOS, so Vulkan must be used through the Molten-VK compatibility layer.
### Installing brew
1. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
2. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"` # set x86_64 brew env
2. Set up the Homebrew shell environment:
1. **On an Intel Mac:** `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
2. **On an Apple Silicon Mac:** eval `"$(/opt/homebrew/bin/brew shellenv)"`
### Installing dependencies
### Installing Tool Dependencies
`brew install boost git cmake llvm ninja nasm molten-vk`
The native versions of these can be used regardless of what type of Mac you have.
`brew install git cmake ninja nasm automake libtool`
### Installing Library Dependencies
**On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used to install these dependencies:**
1. `softwareupdate --install-rosetta` # Install Rosetta 2 if you don't have it. This only has to be done once
2. `arch -x86_64 zsh` # run an x64 shell
3. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
4. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
Then install the dependencies:
`brew install boost molten-vk`
### Build Cemu using CMake
### Build Cemu using cmake and clang
1. `git clone --recursive https://github.com/cemu-project/Cemu`
2. `cd Cemu`
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/local/opt/llvm/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/opt/llvm/bin/clang++ -G Ninja`
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_OSX_ARCHITECTURES=x86_64 -G Ninja`
4. `cmake --build build`
5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`.
@ -104,3 +191,42 @@ You can skip this section if you have an Intel Mac. Every time you compile, you
2. Then, you can rebuild Cemu using the steps listed above, according to whether you use Linux or Windows.
If CMake complains about Cemu already being compiled or another similar error, try deleting the `CMakeCache.txt` file inside the `build` folder and retry building.
## CMake configure flags
Some flags can be passed during CMake configure to customise which features are enabled on build.
Example usage: `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DENABLE_SDL=ON -DENABLE_VULKAN=OFF`
### All platforms
| Flag | | Description | Default | Note |
|--------------------|:--|-----------------------------------------------------------------------------|---------|--------------------|
| ALLOW_PORTABLE | | Allow Cemu to use the `portable` directory to store configs and data | ON | |
| CEMU_CXX_FLAGS | | Flags passed straight to the compiler, e.g. `-march=native`, `-Wall`, `/W3` | "" | |
| ENABLE_CUBEB | | Enable cubeb audio backend | ON | |
| ENABLE_DISCORD_RPC | | Enable Discord Rich presence support | ON | |
| ENABLE_OPENGL | | Enable OpenGL graphics backend | ON | Currently required |
| ENABLE_HIDAPI | | Enable HIDAPI (used for Wiimote controller API) | ON | |
| ENABLE_SDL | | Enable SDLController controller API | ON | Currently required |
| ENABLE_VCPKG | | Use VCPKG package manager to obtain dependencies | ON | |
| ENABLE_VULKAN | | Enable the Vulkan graphics backend | ON | |
| ENABLE_WXWIDGETS | | Enable wxWidgets UI | ON | Currently required |
### Windows
| Flag | Description | Default | Note |
|--------------------|-----------------------------------|---------|--------------------|
| ENABLE_DIRECTAUDIO | Enable DirectAudio audio backend | ON | Currently required |
| ENABLE_DIRECTINPUT | Enable DirectInput controller API | ON | Currently required |
| ENABLE_XAUDIO | Enable XAudio audio backend | ON | |
| ENABLE_XINPUT | Enable XInput controller API | ON | |
### Linux
| Flag | Description | Default |
|-----------------------|----------------------------------------------------|---------|
| ENABLE_BLUEZ | Build with Bluez (used for Wiimote controller API) | ON |
| ENABLE_FERAL_GAMEMODE | Enable Feral Interactive GameMode support | ON |
| ENABLE_WAYLAND | Enable Wayland support | ON |
### macOS
| Flag | Description | Default |
|--------------|------------------------------------------------|---------|
| MACOS_BUNDLE | MacOS executable will be an application bundle | OFF |

View file

@ -1,24 +1,45 @@
cmake_minimum_required(VERSION 3.21.1)
option(ENABLE_VCPKG "Enable the vcpkg package manager" ON)
option(PORTABLE "All data created and maintained by Cemu will be in the directory where the executable file is located" ON)
option(MACOS_BUNDLE "The executable when built on macOS will be created as an application bundle" OFF)
set(EXPERIMENTAL_VERSION "" CACHE STRING "") # used by CI script to set experimental version
option(ALLOW_PORTABLE "Allow Cemu to be run in portable mode" ON)
if (EXPERIMENTAL_VERSION)
add_definitions(-DEMULATOR_VERSION_MINOR=${EXPERIMENTAL_VERSION})
execute_process(
COMMAND git log --format=%h -1
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
endif()
# used by CI script to set version:
set(EMULATOR_VERSION_MAJOR "0" CACHE STRING "")
set(EMULATOR_VERSION_MINOR "0" CACHE STRING "")
set(EMULATOR_VERSION_PATCH "0" CACHE STRING "")
execute_process(
COMMAND git log --format=%h -1
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
if (ENABLE_VCPKG)
# check if vcpkg is shallow and unshallow it if necessary
execute_process(
COMMAND git rev-parse --is-shallow-repository
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/dependencies/vcpkg
OUTPUT_VARIABLE is_vcpkg_shallow
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(is_vcpkg_shallow STREQUAL "true")
message(STATUS "vcpkg is shallow. Unshallowing it now...")
execute_process(
COMMAND git fetch --unshallow
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/dependencies/vcpkg"
RESULT_VARIABLE result
OUTPUT_VARIABLE output
)
endif()
if(UNIX AND NOT APPLE)
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux")
elseif(APPLE)
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_mac")
else()
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
endif()
@ -43,9 +64,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_definitions($<$<CONFIG:Debug>:CEMU_DEBUG_ASSERT>) # if build type is debug, set CEMU_DEBUG_ASSERT
if(PORTABLE)
add_compile_definitions(PORTABLE)
endif()
add_definitions(-DEMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR})
add_definitions(-DEMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR})
add_definitions(-DEMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH})
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@ -72,22 +93,28 @@ endif()
if (APPLE)
enable_language(OBJC OBJCXX)
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0")
endif()
if (UNIX AND NOT APPLE)
option(ENABLE_WAYLAND "Build with Wayland support" ON)
option(ENABLE_FERAL_GAMEMODE "Enables Feral Interactive GameMode Support" ON)
option(ENABLE_BLUEZ "Build with Bluez support" ON)
endif()
option(ENABLE_OPENGL "Enables the OpenGL backend" ON)
option(ENABLE_VULKAN "Enables the Vulkan backend" ON)
option(ENABLE_DISCORD_RPC "Enables the Discord Rich Presence feature" ON)
# input backends
if (WIN32)
option(ENABLE_XINPUT "Enables the usage of XInput" ON)
option(ENABLE_DIRECTINPUT "Enables the usage of DirectInput" ON)
add_compile_definitions(HAS_DIRECTINPUT)
endif()
option(ENABLE_HIDAPI "Build with HIDAPI" ON)
option(ENABLE_SDL "Enables the SDLController backend" ON)
# audio backends
@ -112,7 +139,7 @@ find_package(ZLIB REQUIRED)
find_package(zstd MODULE REQUIRED) # MODULE so that zstd::zstd is available
find_package(OpenSSL COMPONENTS Crypto SSL REQUIRED)
find_package(glm REQUIRED)
find_package(fmt 9.1.0 REQUIRED)
find_package(fmt 9 REQUIRED)
find_package(PNG REQUIRED)
# glslang versions older than 11.11.0 define targets without a namespace
@ -123,10 +150,26 @@ endif()
if (UNIX AND NOT APPLE)
find_package(X11 REQUIRED)
if (ENABLE_WAYLAND)
find_package(Wayland REQUIRED)
find_package(Wayland REQUIRED Client)
find_package(WaylandScanner REQUIRED)
find_package(WaylandProtocols 1.15 REQUIRED)
ecm_add_wayland_client_protocol(WAYLAND_PROTOCOL_SRCS
PROTOCOL "${WaylandProtocols_DATADIR}/stable/viewporter/viewporter.xml"
BASENAME viewporter)
add_library(CemuWaylandProtocols STATIC ${WAYLAND_PROTOCOL_SRCS})
target_include_directories(CemuWaylandProtocols PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
add_compile_definitions(HAS_WAYLAND)
endif()
find_package(GTK3 REQUIRED)
if(ENABLE_BLUEZ)
find_package(bluez REQUIRED)
set(ENABLE_WIIMOTE ON)
add_compile_definitions(HAS_BLUEZ)
endif()
endif()
if (ENABLE_VULKAN)
@ -143,6 +186,20 @@ if (ENABLE_DISCORD_RPC)
target_include_directories(discord-rpc INTERFACE ./dependencies/discord-rpc/include)
endif()
if (ENABLE_HIDAPI)
find_package(hidapi REQUIRED)
set(ENABLE_WIIMOTE ON)
add_compile_definitions(HAS_HIDAPI)
endif ()
if(UNIX AND NOT APPLE)
if(ENABLE_FERAL_GAMEMODE)
add_compile_definitions(ENABLE_FERAL_GAMEMODE)
add_subdirectory(dependencies/gamemode EXCLUDE_FROM_ALL)
target_include_directories(gamemode INTERFACE ./dependencies/gamemode/lib)
endif()
endif()
if (ENABLE_WXWIDGETS)
find_package(wxWidgets 3.2 REQUIRED COMPONENTS base core gl propgrid xrc)
endif()
@ -156,7 +213,7 @@ if (ENABLE_CUBEB)
option(BUILD_TOOLS "" OFF)
option(BUNDLE_SPEEX "" OFF)
set(USE_WINMM OFF CACHE BOOL "")
add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL)
add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL SYSTEM)
set_property(TARGET cubeb PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_library(cubeb::cubeb ALIAS cubeb)
endif()

View file

@ -14,7 +14,7 @@
"generator": "Ninja",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}"
},
{
"name": "Debug",

99
CODING_STYLE.md Normal file
View file

@ -0,0 +1,99 @@
# Coding style guidelines for Cemu
This document describes the latest version of our coding-style guidelines. Since we did not use this style from the beginning, older code may not adhere to these guidelines. Nevertheless, use these rules even if the surrounding code does not match.
Cemu comes with a `.clang-format` file which is supported by most IDEs for formatting. Avoid auto-reformatting whole files, PRs with a lot of formatting changes are difficult to review.
## Names for variables, functions and classes
- Always prefix class member variables with `m_`
- Always prefix static class variables with `s_`
- For variable names: Camel case, starting with a lower case letter after the prefix. Examples: `m_option`, `s_audioVolume`
- For functions/class names: Use camel case starting with a capital letter. Examples: `MyClass`, `SetActive`
- Avoid underscores in variable names after the prefix. Use `m_myVariable` instead of `m_my_variable`
## About types
Cemu provides its own set of basic fixed-width types. They are:
`uint8`, `sint8`, `uint16`, `sint16`, `uint32`, `sint32`, `uint64`, `sint64`. Always use these types over something like `uint32_t`. Using `size_t` is also acceptable where suitable. Avoid C types like `int` or `long`. The only exception is when interacting with external libraries which expect these types as parameters.
## When and where to put brackets
Always put curly-brackets (`{ }`) on their own line. Example:
```
void FooBar()
{
if (m_hasFoo)
{
...
}
}
```
As an exception, you can put short lambdas onto the same line:
```
SomeFunc([]() { .... });
```
You can skip brackets for single-statement `if`. Example:
```
if (cond)
action();
```
## Printing
Avoid sprintf and similar C-style formatting API. Use `fmt::format()`.
In UI related code you can use `formatWxString`, but be aware that number formatting with this function will be locale dependent!
## Strings and encoding
We use UTF-8 encoded `std::string` where possible. Some conversions need special handling and we have helper functions for those:
```cpp
// std::filesystem::path <-> std::string (in precompiled.h)
std::string _pathToUtf8(const fs::path& path);
fs::path _utf8ToPath(std::string_view input);
// wxString <-> std::string
wxString wxString::FromUTF8(const std::string& s)
wxString to_wxString(std::string_view str); // in gui/helpers.h
std::string wxString::utf8_string();
```
## Logging
If you want to write to log.txt use `cemuLog_log()`. The log type parameter should be mostly self-explanatory. Use `LogType::Force` if you always want to log something. For example:
`cemuLog_log(LogType::Force, "The value is {}", 123);`
## HLE and endianness
A pretty large part of Cemu's code base are re-implementations of various Cafe OS modules (e.g. `coreinit.rpl`, `gx2.rpl`...). These generally run in the context of the emulated process, thus special care has to be taken to use types with the correct size and endianness when interacting with memory.
Keep in mind that the emulated Espresso CPU is 32bit big-endian, while the host architectures targeted by Cemu are 64bit little-endian!
To keep code simple and remove the need for manual endian-swapping, Cemu has templates and aliases of the basic types with explicit endian-ness.
For big-endian types add the suffix `be`. Example: `uint32be`
When you need to store a pointer in the guest's memory. Use `MEMPTR<T>`. It will automatically store any pointer as 32bit big-endian. The pointer you store must point to memory that is within the guest address space.
## HLE interfaces
The implementation for each HLE module is inside a namespace with a matching name. E.g. `coreinit.rpl` functions go into `coreinit` namespace.
To expose a new function as callable from within the emulated machine, use `cafeExportRegister` or `cafeExportRegisterFunc`. Here is a short example:
```cpp
namespace coreinit
{
uint32 OSGetCoreCount()
{
return Espresso::CORE_COUNT;
}
void Init()
{
cafeExportRegister("coreinit", OSGetCoreCount, LogType::CoreinitThread);
}
}
```
You may also see some code which uses `osLib_addFunction` directly. This is a deprecated way of registering functions.

View file

@ -5,7 +5,7 @@
[![Matrix Server](https://img.shields.io/matrix/cemu:cemu.info?server_fqdn=matrix.cemu.info&label=cemu:cemu.info&logo=matrix&logoColor=FFFFFF)](https://matrix.to/#/#cemu:cemu.info)
This is the code repository of Cemu, a Wii U emulator that is able to run most Wii U games and homebrew in a playable state.
It's written in C/C++ and is being actively developed with new features and fixes to increase compatibility, convenience and usability.
It's written in C/C++ and is being actively developed with new features and fixes.
Cemu is currently only available for 64-bit Windows, Linux & macOS devices.
@ -24,11 +24,9 @@ Cemu is currently only available for 64-bit Windows, Linux & macOS devices.
## Download
You can download the latest Cemu releases from the [GitHub Releases](https://github.com/cemu-project/Cemu/releases/) or from [Cemu's website](https://cemu.info).
You can download the latest Cemu releases for Windows, Linux and Mac from the [GitHub Releases](https://github.com/cemu-project/Cemu/releases/). For Linux you can also find Cemu on [flathub](https://flathub.org/apps/info.cemu.Cemu).
Cemu is currently only available in a portable format so no installation is required besides extracting it in a safe place.
The native Linux build is currently a work-in-progress. See [Current State Of Linux builds](https://github.com/cemu-project/Cemu/issues/107) for more information about the things to be aware of.
On Windows Cemu is currently only available in a portable format so no installation is required besides extracting it in a safe place.
The native macOS build is currently purely experimental and should not be considered stable or ready for issue-free gameplay. There are also known issues with degraded performance due to the use of MoltenVK and Rosetta for ARM Macs. We appreciate your patience while we improve Cemu for macOS.
@ -36,7 +34,7 @@ Pre-2.0 releases can be found on Cemu's [changelog page](https://cemu.info/chang
## Build Instructions
To compile Cemu yourself on Windows, Linux or macOS, view the [BUILD.md file](/BUILD.md).
To compile Cemu yourself on Windows, Linux or macOS, view [BUILD.md](/BUILD.md).
## Issues
@ -46,10 +44,11 @@ The old bug tracker can be found at [bugs.cemu.info](https://bugs.cemu.info) and
## Contributing
Pull requests are very welcome. For easier coordination you can visit the developer discussion channel on [Discord](https://discord.gg/5psYsup) or alternatively the [Matrix Server](https://matrix.to/#/#cemu:cemu.info).
Before submitting a pull request, please read and follow our code style guidelines listed in [CODING_STYLE.md](/CODING_STYLE.md).
If coding isn't your thing, testing games and making detailed bug reports or updating the (usually outdated) compatibility wiki is also appreciated!
Questions about Cemu's software architecture can also be answered on Discord (through the Matrix bridge).
Questions about Cemu's software architecture can also be answered on Discord (or through the Matrix bridge).
## License
Cemu is licensed under [Mozilla Public License 2.0](/LICENSE.txt). Exempt from this are all files in the dependencies directory for which the licenses of the original code apply as well as some individual files in the src folder, as specified in those file headers respectively.

View file

@ -1,4 +1 @@
# Mario Kart 8 (JPN)
[CPU]
cpuMode = Singlecore-Recompiler
# Mario Kart 8 (JPN)

View file

@ -1,4 +1 @@
# Mario Kart 8 (USA)
[CPU]
cpuMode = Singlecore-Recompiler
# Mario Kart 8 (USA)

View file

@ -1,4 +1 @@
# Mario Kart 8 (EUR)
[CPU]
cpuMode = Singlecore-Recompiler
# Mario Kart 8 (EUR)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/resources/he/cemu.mo Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/resources/uk/cemu.mo Normal file

Binary file not shown.

Binary file not shown.

View file

@ -1 +0,0 @@
If you plan to transfer the shader cache to a different PC or Cemu installation you only need to copy the 'transferable' directory.

View file

@ -0,0 +1,279 @@
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
ECMFindModuleHelpers
--------------------
Helper macros for find modules: ``ecm_find_package_version_check()``,
``ecm_find_package_parse_components()`` and
``ecm_find_package_handle_library_components()``.
::
ecm_find_package_version_check(<name>)
Prints warnings if the CMake version or the project's required CMake version
is older than that required by extra-cmake-modules.
::
ecm_find_package_parse_components(<name>
RESULT_VAR <variable>
KNOWN_COMPONENTS <component1> [<component2> [...]]
[SKIP_DEPENDENCY_HANDLING])
This macro will populate <variable> with a list of components found in
<name>_FIND_COMPONENTS, after checking that all those components are in the
list of ``KNOWN_COMPONENTS``; if there are any unknown components, it will print
an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
``return()``.
The order of components in <variable> is guaranteed to match the order they
are listed in the ``KNOWN_COMPONENTS`` argument.
If ``SKIP_DEPENDENCY_HANDLING`` is not set, for each component the variable
<name>_<component>_component_deps will be checked for dependent components.
If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
dependencies will also be added to <variable>.
::
ecm_find_package_handle_library_components(<name>
COMPONENTS <component> [<component> [...]]
[SKIP_DEPENDENCY_HANDLING])
[SKIP_PKG_CONFIG])
Creates an imported library target for each component. The operation of this
macro depends on the presence of a number of CMake variables.
The <name>_<component>_lib variable should contain the name of this library,
and <name>_<component>_header variable should contain the name of a header
file associated with it (whatever relative path is normally passed to
'#include'). <name>_<component>_header_subdir variable can be used to specify
which subdirectory of the include path the headers will be found in.
``ecm_find_package_components()`` will then search for the library
and include directory (creating appropriate cache variables) and create an
imported library target named <name>::<component>.
Additional variables can be used to provide additional information:
If ``SKIP_PKG_CONFIG``, the <name>_<component>_pkg_config variable is set, and
pkg-config is found, the pkg-config module given by
<name>_<component>_pkg_config will be searched for and used to help locate the
library and header file. It will also be used to set
<name>_<component>_VERSION.
Note that if version information is found via pkg-config,
<name>_<component>_FIND_VERSION can be set to require a particular version
for each component.
If ``SKIP_DEPENDENCY_HANDLING`` is not set, the ``INTERFACE_LINK_LIBRARIES`` property
of the imported target for <component> will be set to contain the imported
targets for the components listed in <name>_<component>_component_deps.
<component>_FOUND will also be set to ``FALSE`` if any of the components in
<name>_<component>_component_deps are not found. This requires the components
in <name>_<component>_component_deps to be listed before <component> in the
``COMPONENTS`` argument.
The following variables will be set:
``<name>_TARGETS``
the imported targets
``<name>_LIBRARIES``
the found libraries
``<name>_INCLUDE_DIRS``
the combined required include directories for the components
``<name>_DEFINITIONS``
the "other" CFLAGS provided by pkg-config, if any
``<name>_VERSION``
the value of ``<name>_<component>_VERSION`` for the first component that
has this variable set (note that components are searched for in the order
they are passed to the macro), although if it is already set, it will not
be altered
.. note::
These variables are never cleared, so if
``ecm_find_package_handle_library_components()`` is called multiple times with
different components (typically because of multiple ``find_package()`` calls) then
``<name>_TARGETS``, for example, will contain all the targets found in any
call (although no duplicates).
Since pre-1.0.0.
#]=======================================================================]
include(CMakeParseArguments)
macro(ecm_find_package_version_check module_name)
if(CMAKE_VERSION VERSION_LESS 3.16.0)
message(FATAL_ERROR "CMake 3.16.0 is required by Find${module_name}.cmake")
endif()
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.16.0)
message(AUTHOR_WARNING "Your project should require at least CMake 3.16.0 to use Find${module_name}.cmake")
endif()
endmacro()
macro(ecm_find_package_parse_components module_name)
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
set(ecm_fppc_oneValueArgs RESULT_VAR)
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
if(ECM_FPPC_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
endif()
if(NOT ECM_FPPC_RESULT_VAR)
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
endif()
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
endif()
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
endif()
if(${module_name}_FIND_COMPONENTS)
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
# Make sure deps are included
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
if("${ecm_fppc_index}" STREQUAL "-1")
if(NOT ${module_name}_FIND_QUIETLY)
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
endif()
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
endif()
endforeach()
endforeach()
else()
message(STATUS "Skipping dependency handling for ${module_name}")
endif()
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
# This makes sure components are listed in the same order as
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
set(${ECM_FPPC_RESULT_VAR})
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
endif()
endforeach()
# if there are any left, they are unknown components
if(ecm_fppc_requestedComps)
set(ecm_fppc_msgType STATUS)
if(${module_name}_FIND_REQUIRED)
set(ecm_fppc_msgType FATAL_ERROR)
endif()
if(NOT ${module_name}_FIND_QUIETLY)
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
endif()
return()
endif()
else()
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
endif()
endmacro()
macro(ecm_find_package_handle_library_components module_name)
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
set(ecm_fpwc_oneValueArgs)
set(ecm_fpwc_multiValueArgs COMPONENTS)
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
if(ECM_FPWC_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
endif()
if(NOT ECM_FPWC_COMPONENTS)
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
endif()
include(FindPackageHandleStandardArgs)
find_package(PkgConfig QUIET)
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
set(ecm_fpwc_dep_vars)
set(ecm_fpwc_dep_targets)
if(NOT SKIP_DEPENDENCY_HANDLING)
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
endforeach()
endif()
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
${${module_name}_${ecm_fpwc_comp}_pkg_config})
endif()
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
)
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
)
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
if(NOT ${module_name}_VERSION)
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
endif()
set(FPHSA_NAME_MISMATCHED 1)
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
FOUND_VAR
${module_name}_${ecm_fpwc_comp}_FOUND
REQUIRED_VARS
${module_name}_${ecm_fpwc_comp}_LIBRARY
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
${ecm_fpwc_dep_vars}
VERSION_VAR
${module_name}_${ecm_fpwc_comp}_VERSION
)
unset(FPHSA_NAME_MISMATCHED)
mark_as_advanced(
${module_name}_${ecm_fpwc_comp}_LIBRARY
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
)
if(${module_name}_${ecm_fpwc_comp}_FOUND)
list(APPEND ${module_name}_LIBRARIES
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
list(APPEND ${module_name}_INCLUDE_DIRS
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
set(${module_name}_DEFINITIONS
${${module_name}_DEFINITIONS}
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
)
endif()
list(APPEND ${module_name}_TARGETS
"${module_name}::${ecm_fpwc_comp}")
endif()
endforeach()
if(${module_name}_LIBRARIES)
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
endif()
if(${module_name}_INCLUDE_DIRS)
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
endif()
if(${module_name}_DEFINITIONS)
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
endif()
if(${module_name}_TARGETS)
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
endif()
endmacro()

View file

@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpers.cmake)

View file

@ -1,14 +1,137 @@
find_package(PkgConfig)
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
# SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
pkg_search_module(WAYLAND_CLIENT IMPORTED_TARGET wayland-client)
#[=======================================================================[.rst:
FindWayland
-----------
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Wayland
REQUIRED_VARS
WAYLAND_CLIENT_LIBRARIES
VERSION_VAR WAYLAND_CLIENT_VERSION
Try to find Wayland.
This is a component-based find module, which makes use of the COMPONENTS
and OPTIONAL_COMPONENTS arguments to find_module. The following components
are available::
Client Server Cursor Egl
If no components are specified, this module will act as though all components
were passed to OPTIONAL_COMPONENTS.
This module will define the following variables, independently of the
components searched for or found:
``Wayland_FOUND``
TRUE if (the requested version of) Wayland is available
``Wayland_VERSION``
Found Wayland version
``Wayland_TARGETS``
A list of all targets imported by this module (note that there may be more
than the components that were requested)
``Wayland_LIBRARIES``
This can be passed to target_link_libraries() instead of the imported
targets
``Wayland_INCLUDE_DIRS``
This should be passed to target_include_directories() if the targets are
not used for linking
``Wayland_DEFINITIONS``
This should be passed to target_compile_options() if the targets are not
used for linking
``Wayland_DATADIR``
The core wayland protocols data directory
Since 5.73.0
For each searched-for components, ``Wayland_<component>_FOUND`` will be set to
TRUE if the corresponding Wayland library was found, and FALSE otherwise. If
``Wayland_<component>_FOUND`` is TRUE, the imported target
``Wayland::<component>`` will be defined. This module will also attempt to
determine ``Wayland_*_VERSION`` variables for each imported target, although
``Wayland_VERSION`` should normally be sufficient.
In general we recommend using the imported targets, as they are easier to use
and provide more control. Bear in mind, however, that if any target is in the
link interface of an exported library, it must be made available by the
package config file.
Since pre-1.0.0.
#]=======================================================================]
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
ecm_find_package_version_check(Wayland)
set(Wayland_known_components
Client
Server
Cursor
Egl
)
foreach(_comp ${Wayland_known_components})
string(TOLOWER "${_comp}" _lc_comp)
set(Wayland_${_comp}_component_deps)
set(Wayland_${_comp}_pkg_config "wayland-${_lc_comp}")
set(Wayland_${_comp}_lib "wayland-${_lc_comp}")
set(Wayland_${_comp}_header "wayland-${_lc_comp}.h")
endforeach()
set(Wayland_Egl_component_deps Client)
ecm_find_package_parse_components(Wayland
RESULT_VAR Wayland_components
KNOWN_COMPONENTS ${Wayland_known_components}
)
ecm_find_package_handle_library_components(Wayland
COMPONENTS ${Wayland_components}
)
if (Wayland_FOUND)
add_library(Wayland::client ALIAS PkgConfig::WAYLAND_CLIENT)
# If pkg-config didn't provide us with version information,
# try to extract it from wayland-version.h
# (Note that the version from wayland-egl.pc will probably be
# the Mesa version, rather than the Wayland version, but that
# version will be ignored as we always find wayland-client.pc
# first).
if(NOT Wayland_VERSION)
find_file(Wayland_VERSION_HEADER
NAMES wayland-version.h
HINTS ${Wayland_INCLUDE_DIRS}
)
mark_as_advanced(Wayland_VERSION_HEADER)
if(Wayland_VERSION_HEADER)
file(READ ${Wayland_VERSION_HEADER} _wayland_version_header_contents)
string(REGEX REPLACE
"^.*[ \t]+WAYLAND_VERSION[ \t]+\"([0-9.]*)\".*$"
"\\1"
Wayland_VERSION
"${_wayland_version_header_contents}"
)
unset(_wayland_version_header_contents)
endif()
endif()
find_package_handle_standard_args(Wayland
FOUND_VAR
Wayland_FOUND
REQUIRED_VARS
Wayland_LIBRARIES
VERSION_VAR
Wayland_VERSION
HANDLE_COMPONENTS
)
pkg_get_variable(Wayland_DATADIR wayland-scanner pkgdatadir)
if (CMAKE_CROSSCOMPILING AND (NOT EXISTS "${Wayland_DATADIR}/wayland.xml"))
# PKG_CONFIG_SYSROOT_DIR only applies to -I and -L flags, so pkg-config
# does not prepend CMAKE_SYSROOT when cross-compiling unless you pass
# --define-prefix explicitly. Therefore we have to manually do prepend
# it here when cross-compiling.
# See https://gitlab.kitware.com/cmake/cmake/-/issues/16647#note_844761
set(Wayland_DATADIR ${CMAKE_SYSROOT}${Wayland_DATADIR})
endif()
if (NOT EXISTS "${Wayland_DATADIR}/wayland.xml")
message(WARNING "Could not find wayland.xml in ${Wayland_DATADIR}")
endif()
include(FeatureSummary)
set_package_properties(Wayland PROPERTIES
URL "https://wayland.freedesktop.org/"
DESCRIPTION "C library implementation of the Wayland protocol: a protocol for a compositor to talk to its clients"
)

View file

@ -0,0 +1,38 @@
# SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
FindWaylandProtocols
--------------------
Try to find wayland-protocols on a Unix system.
This will define the following variables:
``WaylandProtocols_FOUND``
True if (the requested version of) wayland-protocols is available
``WaylandProtocols_VERSION``
The version of wayland-protocols
``WaylandProtocols_DATADIR``
The wayland protocols data directory
#]=======================================================================]
find_package(PkgConfig QUIET)
pkg_check_modules(PKG_wayland_protocols QUIET wayland-protocols)
set(WaylandProtocols_VERSION ${PKG_wayland_protocols_VERSION})
pkg_get_variable(WaylandProtocols_DATADIR wayland-protocols pkgdatadir)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WaylandProtocols
FOUND_VAR WaylandProtocols_FOUND
REQUIRED_VARS WaylandProtocols_DATADIR
VERSION_VAR WaylandProtocols_VERSION
)
include(FeatureSummary)
set_package_properties(WaylandProtocols PROPERTIES
DESCRIPTION "Specifications of extended Wayland protocols"
URL "https://wayland.freedesktop.org/"
)

View file

@ -0,0 +1,162 @@
# SPDX-FileCopyrightText: 2012-2014 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
FindWaylandScanner
------------------
Try to find wayland-scanner.
If the wayland-scanner executable is not in your PATH, you can provide
an alternative name or full path location with the ``WaylandScanner_EXECUTABLE``
variable.
This will define the following variables:
``WaylandScanner_FOUND``
True if wayland-scanner is available.
``WaylandScanner_EXECUTABLE``
The wayland-scanner executable.
If ``WaylandScanner_FOUND`` is TRUE, it will also define the following imported
target:
``Wayland::Scanner``
The wayland-scanner executable.
This module provides the following functions to generate C protocol
implementations:
- ``ecm_add_wayland_client_protocol``
- ``ecm_add_wayland_server_protocol``
::
ecm_add_wayland_client_protocol(<target>
PROTOCOL <xmlfile>
BASENAME <basename>)
ecm_add_wayland_client_protocol(<source_files_var>
PROTOCOL <xmlfile>
BASENAME <basename>)
Generate Wayland client protocol files from ``<xmlfile>`` XML
definition for the ``<basename>`` interface and append those files
to ``<source_files_var>`` or ``<target>``.
::
ecm_add_wayland_server_protocol(<target>
PROTOCOL <xmlfile>
BASENAME <basename>)
ecm_add_wayland_server_protocol(<source_files_var>
PROTOCOL <xmlfile>
BASENAME <basename>)
Generate Wayland server protocol files from ``<xmlfile>`` XML
definition for the ``<basename>`` interface and append those files
to ``<source_files_var>`` or ``<target>``.
Since 1.4.0.
#]=======================================================================]
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
ecm_find_package_version_check(WaylandScanner)
# Find wayland-scanner
find_program(WaylandScanner_EXECUTABLE NAMES wayland-scanner)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WaylandScanner
FOUND_VAR
WaylandScanner_FOUND
REQUIRED_VARS
WaylandScanner_EXECUTABLE
)
mark_as_advanced(WaylandScanner_EXECUTABLE)
if(NOT TARGET Wayland::Scanner AND WaylandScanner_FOUND)
add_executable(Wayland::Scanner IMPORTED)
set_target_properties(Wayland::Scanner PROPERTIES
IMPORTED_LOCATION "${WaylandScanner_EXECUTABLE}"
)
endif()
include(FeatureSummary)
set_package_properties(WaylandScanner PROPERTIES
URL "https://wayland.freedesktop.org/"
DESCRIPTION "Executable that converts XML protocol files to C code"
)
include(CMakeParseArguments)
function(ecm_add_wayland_client_protocol target_or_sources_var)
# Parse arguments
set(oneValueArgs PROTOCOL BASENAME)
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "" ${ARGN})
if(ARGS_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to ecm_add_wayland_client_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
endif()
get_filename_component(_infile ${ARGS_PROTOCOL} ABSOLUTE)
set(_client_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-client-protocol.h")
set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-protocol.c")
set_source_files_properties(${_client_header} GENERATED)
set_source_files_properties(${_code} GENERATED)
set_property(SOURCE ${_client_header} ${_code} PROPERTY SKIP_AUTOMOC ON)
add_custom_command(OUTPUT "${_client_header}"
COMMAND ${WaylandScanner_EXECUTABLE} client-header ${_infile} ${_client_header}
DEPENDS ${_infile} VERBATIM)
add_custom_command(OUTPUT "${_code}"
COMMAND ${WaylandScanner_EXECUTABLE} public-code ${_infile} ${_code}
DEPENDS ${_infile} ${_client_header} VERBATIM)
if (TARGET ${target_or_sources_var})
target_sources(${target_or_sources_var} PRIVATE "${_client_header}" "${_code}")
else()
list(APPEND ${target_or_sources_var} "${_client_header}" "${_code}")
set(${target_or_sources_var} ${${target_or_sources_var}} PARENT_SCOPE)
endif()
endfunction()
function(ecm_add_wayland_server_protocol target_or_sources_var)
# Parse arguments
set(oneValueArgs PROTOCOL BASENAME)
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "" ${ARGN})
if(ARGS_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to ecm_add_wayland_server_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
endif()
ecm_add_wayland_client_protocol(${target_or_sources_var}
PROTOCOL ${ARGS_PROTOCOL}
BASENAME ${ARGS_BASENAME})
get_filename_component(_infile ${ARGS_PROTOCOL} ABSOLUTE)
set(_server_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-server-protocol.h")
set(_server_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-protocol.c")
set_property(SOURCE ${_server_header} ${_server_code} PROPERTY SKIP_AUTOMOC ON)
set_source_files_properties(${_server_header} GENERATED)
add_custom_command(OUTPUT "${_server_header}"
COMMAND ${WaylandScanner_EXECUTABLE} server-header ${_infile} ${_server_header}
DEPENDS ${_infile} VERBATIM)
if (TARGET ${target_or_sources_var})
target_sources(${target_or_sources_var} PRIVATE "${_server_header}")
else()
list(APPEND ${target_or_sources_var} "${_server_header}")
set(${target_or_sources_var} ${${target_or_sources_var}} PARENT_SCOPE)
endif()
endfunction()

20
cmake/Findbluez.cmake Normal file
View file

@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
# SPDX-License-Identifier: ISC
find_package(bluez CONFIG)
if (NOT bluez_FOUND)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(bluez IMPORTED_TARGET GLOBAL bluez-1.0 bluez)
if (bluez_FOUND)
add_library(bluez::bluez ALIAS PkgConfig::bluez)
endif ()
endif ()
endif ()
find_package_handle_standard_args(bluez
REQUIRED_VARS
bluez_LINK_LIBRARIES
bluez_FOUND
VERSION_VAR bluez_VERSION
)

20
cmake/Findlibusb.cmake Normal file
View file

@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
# SPDX-License-Identifier: ISC
find_package(libusb CONFIG)
if (NOT libusb_FOUND)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(libusb IMPORTED_TARGET GLOBAL libusb-1.0 libusb)
if (libusb_FOUND)
add_library(libusb::libusb ALIAS PkgConfig::libusb)
endif ()
endif ()
endif ()
find_package_handle_standard_args(libusb
REQUIRED_VARS
libusb_LINK_LIBRARIES
libusb_FOUND
VERSION_VAR libusb_VERSION
)

4
dependencies/gamemode/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,4 @@
project( gamemode LANGUAGES C )
add_library (gamemode
"lib/gamemode_client.h"
"lib/client_loader.c")

View file

@ -0,0 +1,35 @@
/*
Copyright (c) 2017-2019, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// Simply include the header with GAMEMODE_AUTO set
// This will ensure it calls the functions when it's loaded
#define GAMEMODE_AUTO
#include "gamemode_client.h"

View file

@ -0,0 +1,376 @@
/*
Copyright (c) 2017-2019, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CLIENT_GAMEMODE_H
#define CLIENT_GAMEMODE_H
/*
* GameMode supports the following client functions
* Requests are refcounted in the daemon
*
* int gamemode_request_start() - Request gamemode starts
* 0 if the request was sent successfully
* -1 if the request failed
*
* int gamemode_request_end() - Request gamemode ends
* 0 if the request was sent successfully
* -1 if the request failed
*
* GAMEMODE_AUTO can be defined to make the above two functions apply during static init and
* destruction, as appropriate. In this configuration, errors will be printed to stderr
*
* int gamemode_query_status() - Query the current status of gamemode
* 0 if gamemode is inactive
* 1 if gamemode is active
* 2 if gamemode is active and this client is registered
* -1 if the query failed
*
* int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process
* 0 if the request was sent successfully
* -1 if the request failed
* -2 if the request was rejected
*
* int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process
* 0 if the request was sent successfully
* -1 if the request failed
* -2 if the request was rejected
*
* int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process
* 0 if gamemode is inactive
* 1 if gamemode is active
* 2 if gamemode is active and this client is registered
* -1 if the query failed
*
* const char* gamemode_error_string() - Get an error string
* returns a string describing any of the above errors
*
* Note: All the above requests can be blocking - dbus requests can and will block while the daemon
* handles the request. It is not recommended to make these calls in performance critical code
*/
#include <stdbool.h>
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
static char internal_gamemode_client_error_string[512] = { 0 };
/**
* Load libgamemode dynamically to dislodge us from most dependencies.
* This allows clients to link and/or use this regardless of runtime.
* See SDL2 for an example of the reasoning behind this in terms of
* dynamic versioning as well.
*/
static volatile int internal_libgamemode_loaded = 1;
/* Typedefs for the functions to load */
typedef int (*api_call_return_int)(void);
typedef const char *(*api_call_return_cstring)(void);
typedef int (*api_call_pid_return_int)(pid_t);
/* Storage for functors */
static api_call_return_int REAL_internal_gamemode_request_start = NULL;
static api_call_return_int REAL_internal_gamemode_request_end = NULL;
static api_call_return_int REAL_internal_gamemode_query_status = NULL;
static api_call_return_cstring REAL_internal_gamemode_error_string = NULL;
static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL;
static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL;
static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL;
/**
* Internal helper to perform the symbol binding safely.
*
* Returns 0 on success and -1 on failure
*/
__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(
void *handle, const char *name, void **out_func, size_t func_size, bool required)
{
void *symbol_lookup = NULL;
char *dl_error = NULL;
/* Safely look up the symbol */
symbol_lookup = dlsym(handle, name);
dl_error = dlerror();
if (required && (dl_error || !symbol_lookup)) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"dlsym failed - %s",
dl_error);
return -1;
}
/* Have the symbol correctly, copy it to make it usable */
memcpy(out_func, &symbol_lookup, func_size);
return 0;
}
/**
* Loads libgamemode and needed functions
*
* Returns 0 on success and -1 on failure
*/
__attribute__((always_inline)) static inline int internal_load_libgamemode(void)
{
/* We start at 1, 0 is a success and -1 is a fail */
if (internal_libgamemode_loaded != 1) {
return internal_libgamemode_loaded;
}
/* Anonymous struct type to define our bindings */
struct binding {
const char *name;
void **functor;
size_t func_size;
bool required;
} bindings[] = {
{ "real_gamemode_request_start",
(void **)&REAL_internal_gamemode_request_start,
sizeof(REAL_internal_gamemode_request_start),
true },
{ "real_gamemode_request_end",
(void **)&REAL_internal_gamemode_request_end,
sizeof(REAL_internal_gamemode_request_end),
true },
{ "real_gamemode_query_status",
(void **)&REAL_internal_gamemode_query_status,
sizeof(REAL_internal_gamemode_query_status),
false },
{ "real_gamemode_error_string",
(void **)&REAL_internal_gamemode_error_string,
sizeof(REAL_internal_gamemode_error_string),
true },
{ "real_gamemode_request_start_for",
(void **)&REAL_internal_gamemode_request_start_for,
sizeof(REAL_internal_gamemode_request_start_for),
false },
{ "real_gamemode_request_end_for",
(void **)&REAL_internal_gamemode_request_end_for,
sizeof(REAL_internal_gamemode_request_end_for),
false },
{ "real_gamemode_query_status_for",
(void **)&REAL_internal_gamemode_query_status_for,
sizeof(REAL_internal_gamemode_query_status_for),
false },
};
void *libgamemode = NULL;
/* Try and load libgamemode */
libgamemode = dlopen("libgamemode.so.0", RTLD_NOW);
if (!libgamemode) {
/* Attempt to load unversioned library for compatibility with older
* versions (as of writing, there are no ABI changes between the two -
* this may need to change if ever ABI-breaking changes are made) */
libgamemode = dlopen("libgamemode.so", RTLD_NOW);
if (!libgamemode) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"dlopen failed - %s",
dlerror());
internal_libgamemode_loaded = -1;
return -1;
}
}
/* Attempt to bind all symbols */
for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) {
struct binding *binder = &bindings[i];
if (internal_bind_libgamemode_symbol(libgamemode,
binder->name,
binder->functor,
binder->func_size,
binder->required)) {
internal_libgamemode_loaded = -1;
return -1;
};
}
/* Success */
internal_libgamemode_loaded = 0;
return 0;
}
/**
* Redirect to the real libgamemode
*/
__attribute__((always_inline)) static inline const char *gamemode_error_string(void)
{
/* If we fail to load the system gamemode, or we have an error string already, return our error
* string instead of diverting to the system version */
if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') {
return internal_gamemode_client_error_string;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_error_string != NULL);
return REAL_internal_gamemode_error_string();
}
/**
* Redirect to the real libgamemode
* Allow automatically requesting game mode
* Also prints errors as they happen.
*/
#ifdef GAMEMODE_AUTO
__attribute__((constructor))
#else
__attribute__((always_inline)) static inline
#endif
int gamemode_request_start(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_request_start != NULL);
if (REAL_internal_gamemode_request_start() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
return 0;
}
/* Redirect to the real libgamemode */
#ifdef GAMEMODE_AUTO
__attribute__((destructor))
#else
__attribute__((always_inline)) static inline
#endif
int gamemode_request_end(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_request_end != NULL);
if (REAL_internal_gamemode_request_end() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
return 0;
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_query_status(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_query_status == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_query_status missing (older host?)");
return -1;
}
return REAL_internal_gamemode_query_status();
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_request_start_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_request_start_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_request_start_for(pid);
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_request_end_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_request_end_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_request_end_for(pid);
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_query_status_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_query_status_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_query_status_for(pid);
}
#endif // CLIENT_GAMEMODE_H

View file

@ -117,7 +117,13 @@ add_library (ih264d
"decoder/ivd.h"
)
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
if (CMAKE_OSX_ARCHITECTURES)
set(IH264D_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
else()
set(IH264D_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
if (IH264D_ARCHITECTURE STREQUAL "x86_64" OR IH264D_ARCHITECTURE STREQUAL "amd64" OR IH264D_ARCHITECTURE STREQUAL "AMD64")
set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86")
include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES})
target_sources(ih264d PRIVATE
@ -140,7 +146,7 @@ target_sources(ih264d PRIVATE
"decoder/x86/ih264d_function_selector_sse42.c"
"decoder/x86/ih264d_function_selector_ssse3.c"
)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
elseif(IH264D_ARCHITECTURE STREQUAL "aarch64" OR IH264D_ARCHITECTURE STREQUAL "arm64")
enable_language( C CXX ASM )
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
@ -178,9 +184,15 @@ target_sources(ih264d PRIVATE
)
target_compile_options(ih264d PRIVATE -DARMV8)
else()
message(FATAL_ERROR "ih264d unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
endif()
if(MSVC)
set_property(TARGET ih264d PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# tune settings for slightly better performance
target_compile_options(ih264d PRIVATE $<$<CONFIG:Release,RelWithDebInfo>:/Oi>) # enable intrinsic functions
target_compile_options(ih264d PRIVATE $<$<CONFIG:Release,RelWithDebInfo>:/Ot>) # favor speed
target_compile_options(ih264d PRIVATE "/GS-") # disable runtime checks
endif()

View file

@ -85,28 +85,59 @@ UWORD32 ithread_get_mutex_lock_size(void)
return sizeof(CRITICAL_SECTION);
}
struct _ithread_launch_param
{
void (*startFunc)(void* argument);
void* argument;
};
DWORD WINAPI _ithread_WinThreadStartRoutine(LPVOID lpThreadParameter)
{
struct _ithread_launch_param* param = (struct _ithread_launch_param*)lpThreadParameter;
typedef void *(*ThreadStartRoutineType)(void *);
ThreadStartRoutineType pfnThreadRoutine = (ThreadStartRoutineType)param->startFunc;
void* arg = param->argument;
free(param);
pfnThreadRoutine(arg);
return 0;
}
WORD32 ithread_create(void* thread_handle, void* attribute, void* strt, void* argument)
{
//UNUSED(attribute);
//return pthread_create((pthread_t*)thread_handle, NULL, (void* (*)(void*)) strt, argument);
__debugbreak();
UNUSED(attribute);
struct _ithread_launch_param* param = malloc(sizeof(struct _ithread_launch_param));
param->startFunc = (void (*)(void*))strt;
param->argument = argument;
HANDLE *handle = (HANDLE*)thread_handle;
*handle = CreateThread(NULL, 0, _ithread_WinThreadStartRoutine, param, 0, NULL);
if(*handle == NULL)
{
return -1;
}
return 0;
}
WORD32 ithread_join(void* thread_handle, void** val_ptr)
{
//UNUSED(val_ptr);
//pthread_t* pthread_handle = (pthread_t*)thread_handle;
//return pthread_join(*pthread_handle, NULL);
__debugbreak();
return 0;
HANDLE *handle = (HANDLE*)thread_handle;
DWORD result = WaitForSingleObject(*handle, INFINITE);
if(result == WAIT_OBJECT_0)
{
CloseHandle(*handle);
return 0;
}
else
{
return -1;
}
}
WORD32 ithread_get_mutex_struct_size(void)
{
return sizeof(CRITICAL_SECTION);
}
WORD32 ithread_mutex_init(void* mutex)
{
InitializeCriticalSection((LPCRITICAL_SECTION)mutex);
@ -153,7 +184,6 @@ UWORD32 ithread_get_sem_struct_size(void)
//return(sizeof(sem_t));
}
WORD32 ithread_sem_init(void* sem, WORD32 pshared, UWORD32 value)
{
__debugbreak();
@ -168,7 +198,6 @@ WORD32 ithread_sem_post(void* sem)
//return sem_post((sem_t*)sem);
}
WORD32 ithread_sem_wait(void* sem)
{
__debugbreak();
@ -176,7 +205,6 @@ WORD32 ithread_sem_wait(void* sem)
//return sem_wait((sem_t*)sem);
}
WORD32 ithread_sem_destroy(void* sem)
{
__debugbreak();

View file

@ -79,10 +79,8 @@
static inline int __builtin_clz(unsigned x)
{
unsigned long n;
if (x == 0)
return 32;
_BitScanReverse(&n, x);
return 31 - n;
return n ^ 31;
}
static inline int __builtin_ctz(unsigned x) {

2
dependencies/vcpkg vendored

@ -1 +1 @@
Subproject commit b81bc3a83fdbdffe80325eeabb2ec735a1f3c29d
Subproject commit a4275b7eee79fb24ec2e135481ef5fce8b41c339

View file

@ -1,13 +0,0 @@
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index 65a98efbe..2f99f28f1 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -352,7 +352,7 @@ endmacro()
# - HAVE_SDL_LOADSO opt
macro(CheckLibSampleRate)
if(SDL_LIBSAMPLERATE)
- find_package(SampleRate QUIET)
+ find_package(SampleRate CONFIG REQUIRED)
if(SampleRate_FOUND AND TARGET SampleRate::samplerate)
set(HAVE_LIBSAMPLERATE TRUE)
set(HAVE_LIBSAMPLERATE_H TRUE)

View file

@ -1,130 +0,0 @@
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO libsdl-org/SDL
REF "release-${VERSION}"
SHA512 90858ae8c5fdddd5e13724e05ad0970e11bbab1df8a0201c3f4ce354dc6018e5d4ab7279402a263c716aacdaa52745f78531dc225d48d790ee9307e2f6198695
HEAD_REF main
PATCHES
deps.patch
)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" SDL_STATIC)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" SDL_SHARED)
string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" FORCE_STATIC_VCRT)
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
FEATURES
vulkan SDL_VULKAN
x11 SDL_X11
wayland SDL_WAYLAND
samplerate SDL_LIBSAMPLERATE
ibus SDL_IBUS
)
if ("x11" IN_LIST FEATURES)
message(WARNING "You will need to install Xorg dependencies to use feature x11:\nsudo apt install libx11-dev libxft-dev libxext-dev\n")
endif()
if ("wayland" IN_LIST FEATURES)
message(WARNING "You will need to install Wayland dependencies to use feature wayland:\nsudo apt install libwayland-dev libxkbcommon-dev libegl1-mesa-dev\n")
endif()
if ("ibus" IN_LIST FEATURES)
message(WARNING "You will need to install ibus dependencies to use feature ibus:\nsudo apt install libibus-1.0-dev\n")
endif()
if(VCPKG_TARGET_IS_UWP)
set(configure_opts WINDOWS_USE_MSBUILD)
endif()
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
${configure_opts}
OPTIONS ${FEATURE_OPTIONS}
-DSDL_STATIC=${SDL_STATIC}
-DSDL_SHARED=${SDL_SHARED}
-DSDL_FORCE_STATIC_VCRT=${FORCE_STATIC_VCRT}
-DSDL_LIBC=ON
-DSDL_TEST=OFF
-DSDL_INSTALL_CMAKEDIR="cmake"
-DCMAKE_DISABLE_FIND_PACKAGE_Git=ON
-DSDL_LIBSAMPLERATE_SHARED=OFF
MAYBE_UNUSED_VARIABLES
SDL_FORCE_STATIC_VCRT
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH cmake)
file(REMOVE_RECURSE
"${CURRENT_PACKAGES_DIR}/debug/include"
"${CURRENT_PACKAGES_DIR}/debug/share"
"${CURRENT_PACKAGES_DIR}/bin/sdl2-config"
"${CURRENT_PACKAGES_DIR}/debug/bin/sdl2-config"
"${CURRENT_PACKAGES_DIR}/SDL2.framework"
"${CURRENT_PACKAGES_DIR}/debug/SDL2.framework"
"${CURRENT_PACKAGES_DIR}/share/licenses"
"${CURRENT_PACKAGES_DIR}/share/aclocal"
)
file(GLOB BINS "${CURRENT_PACKAGES_DIR}/debug/bin/*" "${CURRENT_PACKAGES_DIR}/bin/*")
if(NOT BINS)
file(REMOVE_RECURSE
"${CURRENT_PACKAGES_DIR}/bin"
"${CURRENT_PACKAGES_DIR}/debug/bin"
)
endif()
if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_UWP AND NOT VCPKG_TARGET_IS_MINGW)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib/manual-link")
file(RENAME "${CURRENT_PACKAGES_DIR}/lib/SDL2main.lib" "${CURRENT_PACKAGES_DIR}/lib/manual-link/SDL2main.lib")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link")
file(RENAME "${CURRENT_PACKAGES_DIR}/debug/lib/SDL2maind.lib" "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link/SDL2maind.lib")
endif()
file(GLOB SHARE_FILES "${CURRENT_PACKAGES_DIR}/share/sdl2/*.cmake")
foreach(SHARE_FILE ${SHARE_FILES})
vcpkg_replace_string("${SHARE_FILE}" "lib/SDL2main" "lib/manual-link/SDL2main")
endforeach()
endif()
vcpkg_copy_pdbs()
set(DYLIB_COMPATIBILITY_VERSION_REGEX "set\\(DYLIB_COMPATIBILITY_VERSION (.+)\\)")
set(DYLIB_CURRENT_VERSION_REGEX "set\\(DYLIB_CURRENT_VERSION (.+)\\)")
file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_COMPATIBILITY_VERSION REGEX ${DYLIB_COMPATIBILITY_VERSION_REGEX})
file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_CURRENT_VERSION REGEX ${DYLIB_CURRENT_VERSION_REGEX})
string(REGEX REPLACE ${DYLIB_COMPATIBILITY_VERSION_REGEX} "\\1" DYLIB_COMPATIBILITY_VERSION "${DYLIB_COMPATIBILITY_VERSION}")
string(REGEX REPLACE ${DYLIB_CURRENT_VERSION_REGEX} "\\1" DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION}")
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2main" "-lSDL2maind")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2 " "-lSDL2d ")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-static " "-lSDL2-staticd ")
endif()
if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic" AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-lSDL2-static " " ")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-staticd " " ")
endif()
endif()
if(VCPKG_TARGET_IS_UWP)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "$<$<CONFIG:Debug>:d>.lib" "")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "$<$<CONFIG:Debug>:d>.lib" "d")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:")
endif()
endif()
vcpkg_fixup_pkgconfig()
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE.txt")

View file

@ -1,8 +0,0 @@
sdl2 provides CMake targets:
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(main
PRIVATE
$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>
$<IF:$<TARGET_EXISTS:SDL2::SDL2>,SDL2::SDL2,SDL2::SDL2-static>
)

View file

@ -1,58 +0,0 @@
{
"name": "sdl2",
"version": "2.26.5",
"description": "Simple DirectMedia Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.",
"homepage": "https://www.libsdl.org/download-2.0.php",
"license": "Zlib",
"dependencies": [
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
],
"default-features": [
"base"
],
"features": {
"base": {
"description": "Base functionality for SDL",
"dependencies": [
{
"name": "sdl2",
"default-features": false,
"features": [
"ibus",
"wayland",
"x11"
],
"platform": "linux"
}
]
},
"ibus": {
"description": "Build with ibus IME support",
"supports": "linux"
},
"samplerate": {
"description": "Use libsamplerate for audio rate conversion",
"dependencies": [
"libsamplerate"
]
},
"vulkan": {
"description": "Vulkan functionality for SDL"
},
"wayland": {
"description": "Build with Wayland support",
"supports": "linux"
},
"x11": {
"description": "Build with X11 support",
"supports": "!windows"
}
}
}

View file

@ -1,13 +0,0 @@
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index 65a98efbe..2f99f28f1 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -352,7 +352,7 @@ endmacro()
# - HAVE_SDL_LOADSO opt
macro(CheckLibSampleRate)
if(SDL_LIBSAMPLERATE)
- find_package(SampleRate QUIET)
+ find_package(SampleRate CONFIG REQUIRED)
if(SampleRate_FOUND AND TARGET SampleRate::samplerate)
set(HAVE_LIBSAMPLERATE TRUE)
set(HAVE_LIBSAMPLERATE_H TRUE)

View file

@ -1,130 +0,0 @@
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO libsdl-org/SDL
REF "release-${VERSION}"
SHA512 90858ae8c5fdddd5e13724e05ad0970e11bbab1df8a0201c3f4ce354dc6018e5d4ab7279402a263c716aacdaa52745f78531dc225d48d790ee9307e2f6198695
HEAD_REF main
PATCHES
deps.patch
)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" SDL_STATIC)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" SDL_SHARED)
string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" FORCE_STATIC_VCRT)
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
FEATURES
vulkan SDL_VULKAN
x11 SDL_X11
wayland SDL_WAYLAND
samplerate SDL_LIBSAMPLERATE
ibus SDL_IBUS
)
if ("x11" IN_LIST FEATURES)
message(WARNING "You will need to install Xorg dependencies to use feature x11:\nsudo apt install libx11-dev libxft-dev libxext-dev\n")
endif()
if ("wayland" IN_LIST FEATURES)
message(WARNING "You will need to install Wayland dependencies to use feature wayland:\nsudo apt install libwayland-dev libxkbcommon-dev libegl1-mesa-dev\n")
endif()
if ("ibus" IN_LIST FEATURES)
message(WARNING "You will need to install ibus dependencies to use feature ibus:\nsudo apt install libibus-1.0-dev\n")
endif()
if(VCPKG_TARGET_IS_UWP)
set(configure_opts WINDOWS_USE_MSBUILD)
endif()
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
${configure_opts}
OPTIONS ${FEATURE_OPTIONS}
-DSDL_STATIC=${SDL_STATIC}
-DSDL_SHARED=${SDL_SHARED}
-DSDL_FORCE_STATIC_VCRT=${FORCE_STATIC_VCRT}
-DSDL_LIBC=ON
-DSDL_TEST=OFF
-DSDL_INSTALL_CMAKEDIR="cmake"
-DCMAKE_DISABLE_FIND_PACKAGE_Git=ON
-DSDL_LIBSAMPLERATE_SHARED=OFF
MAYBE_UNUSED_VARIABLES
SDL_FORCE_STATIC_VCRT
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH cmake)
file(REMOVE_RECURSE
"${CURRENT_PACKAGES_DIR}/debug/include"
"${CURRENT_PACKAGES_DIR}/debug/share"
"${CURRENT_PACKAGES_DIR}/bin/sdl2-config"
"${CURRENT_PACKAGES_DIR}/debug/bin/sdl2-config"
"${CURRENT_PACKAGES_DIR}/SDL2.framework"
"${CURRENT_PACKAGES_DIR}/debug/SDL2.framework"
"${CURRENT_PACKAGES_DIR}/share/licenses"
"${CURRENT_PACKAGES_DIR}/share/aclocal"
)
file(GLOB BINS "${CURRENT_PACKAGES_DIR}/debug/bin/*" "${CURRENT_PACKAGES_DIR}/bin/*")
if(NOT BINS)
file(REMOVE_RECURSE
"${CURRENT_PACKAGES_DIR}/bin"
"${CURRENT_PACKAGES_DIR}/debug/bin"
)
endif()
if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_UWP AND NOT VCPKG_TARGET_IS_MINGW)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib/manual-link")
file(RENAME "${CURRENT_PACKAGES_DIR}/lib/SDL2main.lib" "${CURRENT_PACKAGES_DIR}/lib/manual-link/SDL2main.lib")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link")
file(RENAME "${CURRENT_PACKAGES_DIR}/debug/lib/SDL2maind.lib" "${CURRENT_PACKAGES_DIR}/debug/lib/manual-link/SDL2maind.lib")
endif()
file(GLOB SHARE_FILES "${CURRENT_PACKAGES_DIR}/share/sdl2/*.cmake")
foreach(SHARE_FILE ${SHARE_FILES})
vcpkg_replace_string("${SHARE_FILE}" "lib/SDL2main" "lib/manual-link/SDL2main")
endforeach()
endif()
vcpkg_copy_pdbs()
set(DYLIB_COMPATIBILITY_VERSION_REGEX "set\\(DYLIB_COMPATIBILITY_VERSION (.+)\\)")
set(DYLIB_CURRENT_VERSION_REGEX "set\\(DYLIB_CURRENT_VERSION (.+)\\)")
file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_COMPATIBILITY_VERSION REGEX ${DYLIB_COMPATIBILITY_VERSION_REGEX})
file(STRINGS "${SOURCE_PATH}/CMakeLists.txt" DYLIB_CURRENT_VERSION REGEX ${DYLIB_CURRENT_VERSION_REGEX})
string(REGEX REPLACE ${DYLIB_COMPATIBILITY_VERSION_REGEX} "\\1" DYLIB_COMPATIBILITY_VERSION "${DYLIB_COMPATIBILITY_VERSION}")
string(REGEX REPLACE ${DYLIB_CURRENT_VERSION_REGEX} "\\1" DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION}")
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2main" "-lSDL2maind")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2 " "-lSDL2d ")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-static " "-lSDL2-staticd ")
endif()
if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic" AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-lSDL2-static " " ")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-lSDL2-staticd " " ")
endif()
endif()
if(VCPKG_TARGET_IS_UWP)
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "$<$<CONFIG:Debug>:d>.lib" "")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:")
endif()
if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "$<$<CONFIG:Debug>:d>.lib" "d")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/sdl2.pc" "-l-nodefaultlib:" "-nodefaultlib:")
endif()
endif()
vcpkg_fixup_pkgconfig()
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE.txt")

View file

@ -1,8 +0,0 @@
sdl2 provides CMake targets:
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(main
PRIVATE
$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>
$<IF:$<TARGET_EXISTS:SDL2::SDL2>,SDL2::SDL2,SDL2::SDL2-static>
)

View file

@ -1,58 +0,0 @@
{
"name": "sdl2",
"version": "2.26.5",
"description": "Simple DirectMedia Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.",
"homepage": "https://www.libsdl.org/download-2.0.php",
"license": "Zlib",
"dependencies": [
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
],
"default-features": [
"base"
],
"features": {
"base": {
"description": "Base functionality for SDL",
"dependencies": [
{
"name": "sdl2",
"default-features": false,
"features": [
"ibus",
"wayland",
"x11"
],
"platform": "linux"
}
]
},
"ibus": {
"description": "Build with ibus IME support",
"supports": "linux"
},
"samplerate": {
"description": "Use libsamplerate for audio rate conversion",
"dependencies": [
"libsamplerate"
]
},
"vulkan": {
"description": "Vulkan functionality for SDL"
},
"wayland": {
"description": "Build with Wayland support",
"supports": "linux"
},
"x11": {
"description": "Build with X11 support",
"supports": "!windows"
}
}
}

View file

@ -0,0 +1,71 @@
set(VCPKG_LIBRARY_LINKAGE dynamic)
if(VCPKG_TARGET_IS_LINUX)
message("${PORT} currently requires the following tools and libraries from the system package manager:\n autoreconf\n libudev\n\nThese can be installed on Ubuntu systems via apt-get install autoconf libudev-dev")
endif()
set(VERSION 1.0.26)
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO libusb/libusb
REF fcf0c710ef5911ae37fbbf1b39d48a89f6f14e8a # v1.0.26.11791 2023-03-12
SHA512 0aa6439f7988487adf2a3bff473fec80b5c722a47f117a60696d2aa25c87cc3f20fb6aaca7c66e49be25db6a35eb0bb5f71ed7b211d1b8ee064c5d7f1b985c73
HEAD_REF master
)
if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic")
set(LIBUSB_PROJECT_TYPE dll)
else()
set(LIBUSB_PROJECT_TYPE static)
endif()
# The README.md file in the archive is a symlink to README
# which causes issues with the windows MSBUILD process
file(REMOVE "${SOURCE_PATH}/README.md")
vcpkg_msbuild_install(
SOURCE_PATH "${SOURCE_PATH}"
PROJECT_SUBPATH msvc/libusb_${LIBUSB_PROJECT_TYPE}.vcxproj
)
file(INSTALL "${SOURCE_PATH}/libusb/libusb.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include/libusb-1.0")
set(prefix "")
set(exec_prefix [[${prefix}]])
set(libdir [[${prefix}/lib]])
set(includedir [[${prefix}/include]])
configure_file("${SOURCE_PATH}/libusb-1.0.pc.in" "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/libusb-1.0.pc" @ONLY)
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/libusb-1.0.pc" " -lusb-1.0" " -llibusb-1.0")
if(NOT VCPKG_BUILD_TYPE)
set(includedir [[${prefix}/../include]])
configure_file("${SOURCE_PATH}/libusb-1.0.pc.in" "${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/libusb-1.0.pc" @ONLY)
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/libusb-1.0.pc" " -lusb-1.0" " -llibusb-1.0")
endif()
else()
vcpkg_list(SET MAKE_OPTIONS)
vcpkg_list(SET LIBUSB_LINK_LIBRARIES)
if(VCPKG_TARGET_IS_EMSCRIPTEN)
vcpkg_list(APPEND MAKE_OPTIONS BUILD_TRIPLET --host=wasm32)
endif()
if("udev" IN_LIST FEATURES)
vcpkg_list(APPEND MAKE_OPTIONS "--enable-udev")
vcpkg_list(APPEND LIBUSB_LINK_LIBRARIES udev)
else()
vcpkg_list(APPEND MAKE_OPTIONS "--disable-udev")
endif()
vcpkg_configure_make(
SOURCE_PATH "${SOURCE_PATH}"
AUTOCONFIG
OPTIONS
${MAKE_OPTIONS}
"--enable-examples-build=no"
"--enable-tests-build=no"
)
vcpkg_install_make()
endif()
vcpkg_fixup_pkgconfig()
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING")

View file

@ -0,0 +1,5 @@
libusb can be imported via CMake FindPkgConfig module:
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
target_link_libraries(main PRIVATE PkgConfig::libusb)

View file

@ -0,0 +1,8 @@
{
"name": "libusb",
"version": "1.0.26.11791",
"port-version": 7,
"description": "a cross-platform library to access USB devices",
"homepage": "https://github.com/libusb/libusb",
"license": "LGPL-2.1-or-later"
}

View file

@ -10,6 +10,8 @@ curl -sSfL https://github.com"$(curl https://github.com/probonopd/go-appimage/re
chmod a+x mkappimage.AppImage
curl -sSfLO "https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh"
chmod a+x linuxdeploy-plugin-gtk.sh
curl -sSfLO "https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh"
chmod a+x linuxdeploy-plugin-checkrt.sh
if [[ ! -e /usr/lib/x86_64-linux-gnu ]]; then
sed -i 's#lib\/x86_64-linux-gnu#lib64#g' linuxdeploy-plugin-gtk.sh
@ -33,12 +35,14 @@ chmod +x AppDir/usr/bin/Cemu
cp /usr/lib/x86_64-linux-gnu/{libsepol.so.1,libffi.so.7,libpcre.so.3,libGLU.so.1,libthai.so.0} AppDir/usr/lib
export UPD_INFO="gh-releases-zsync|cemu-project|Cemu|ci|Cemu.AppImage.zsync"
export NO_STRIP=1
./linuxdeploy-x86_64.AppImage --appimage-extract-and-run \
--appdir="${GITHUB_WORKSPACE}"/AppDir/ \
-d "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.desktop \
-i "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.png \
-e "${GITHUB_WORKSPACE}"/AppDir/usr/bin/Cemu \
--plugin gtk
--plugin gtk \
--plugin checkrt
if ! GITVERSION="$(git rev-parse --short HEAD 2>/dev/null)"; then
GITVERSION=experimental

View file

@ -3,15 +3,15 @@
<!--Created with jdAppdataEdit 4.2-->
<id>info.cemu.Cemu</id>
<name>Cemu</name>
<summary>Software to emulate Wii U games and applications on PC</summary>
<summary xml:lang="de">Software zum emulieren von Wii U Spielen und Anwendungen auf dem PC</summary>
<summary xml:lang="fr">Application pour émuler des jeux et applications Wii U sur PC</summary>
<summary xml:lang="nl">Applicatie om Wii U spellen en applicaties te emuleren op PC</summary>
<summary xml:lang="el">Πρόγραμμα προσομοίωσης παιχνιδιών και εφαρμογών Wii U στον υπολογιστή</summary>
<summary xml:lang="es">Software para emular juegos y aplicaciones de Wii U en PC</summary>
<summary xml:lang="pt_BR">Software para emular jogos e aplicativos de Wii U no PC</summary>
<summary xml:lang="it">Software per emulare giochi e applicazioni per Wii U su PC</summary>
<summary xml:lang="fi">Ojelmisto Wii U -pelien ja -sovellusten emulointiin PC:llä</summary>
<summary>Nintendo Wii U Emulator</summary>
<summary xml:lang="de">Nintendo Wii U Emulator</summary>
<summary xml:lang="fr">Émulateur Nintendo Wii U</summary>
<summary xml:lang="nl">Nintendo Wii U Emulator</summary>
<summary xml:lang="el">Εξομοιωτής Nintendo Wii U</summary>
<summary xml:lang="es">Emulador de Nintendo Wii U</summary>
<summary xml:lang="pt_BR">Emulador Nintendo Wii U</summary>
<summary xml:lang="it">Emulatore Nintendo Wii U</summary>
<summary xml:lang="fi">Nintendo Wii U Emulaattori</summary>
<developer_name>Cemu Project</developer_name>
<launchable type="desktop-id">info.cemu.Cemu.desktop</launchable>
<metadata_license>CC0-1.0</metadata_license>

17
dist/network_services.xml vendored Normal file
View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<content>
<networkname>CustomExample</networkname>
<disablesslverification>0</disablesslverification>
<urls>
<act>https://account.nintendo.net</act>
<ecs>https://ecs.wup.shop.nintendo.net/ecs/services/ECommerceSOAP</ecs>
<nus>https://nus.wup.shop.nintendo.net/nus/services/NetUpdateSOAP</nus>
<ias>https://ias.wup.shop.nintendo.net/ias/services/IdentityAuthenticationSOAP</ias>
<ccsu>https://ccs.wup.shop.nintendo.net/ccs/download</ccsu>
<ccs>http://ccs.cdn.wup.shop.nintendo.net/ccs/download</ccs>
<idbe>https://idbe-wup.cdn.nintendo.net/icondata</idbe>
<boss>https://npts.app.nintendo.net/p01/tasksheet</boss>
<tagaya>https://tagaya.wup.shop.nintendo.net/tagaya/versionlist</tagaya>
<olv>https://discovery.olv.nintendo.net/v1/endpoint</olv>
</urls>
</content>

16
dist/windows/Cemu.manifest vendored Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel></requestedPrivileges></security>
</trustInfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
</windowsSettings>
</application>
</assembly>

View file

@ -1,2 +0,0 @@
"C:\PROGRAM FILES\MICROSOFT VISUAL STUDIO\2022\COMMUNITY\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\CMake\bin\cmake.exe" -B build/
pause

View file

@ -56,10 +56,17 @@ add_executable(CemuBin
mainLLE.cpp
)
if(MSVC AND MSVC_VERSION EQUAL 1940)
# workaround for an msvc issue on VS 17.10 where generated ILK files are too large
# see https://developercommunity.visualstudio.com/t/After-updating-to-VS-1710-the-size-of-/10665511
set_target_properties(CemuBin PROPERTIES LINK_FLAGS "/INCREMENTAL:NO")
endif()
if(WIN32)
target_sources(CemuBin PRIVATE
resource/cemu.rc
)
../dist/windows/cemu.manifest
)
endif()
set_property(TARGET CemuBin PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
@ -75,11 +82,13 @@ if (MACOS_BUNDLE)
set(MACOSX_BUNDLE_ICON_FILE "cemu.icns")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "info.cemu.Cemu")
set(MACOSX_BUNDLE_BUNDLE_NAME "Cemu")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${CMAKE_PROJECT_VERSION})
set(MACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION})
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2022 Cemu Project")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}")
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2024 Cemu Project")
set(MACOSX_BUNDLE_CATEGORY "public.app-category.games")
set(MACOSX_MINIMUM_SYSTEM_VERSION "12.0")
set(MACOSX_BUNDLE_TYPE_EXTENSION "wua")
set_target_properties(CemuBin PROPERTIES
MACOSX_BUNDLE true
@ -92,11 +101,18 @@ if (MACOS_BUNDLE)
COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory "${CMAKE_SOURCE_DIR}/bin/${folder}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/SharedSupport/${folder}")
endforeach(folder)
add_custom_command (TARGET CemuBin POST_BUILD
COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/debug/lib/libusb-1.0.0.dylib")
else()
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/lib/libusb-1.0.0.dylib")
endif()
add_custom_command (TARGET CemuBin POST_BUILD
COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}")
COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib"
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${LIBUSB_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libusb-1.0.0.dylib"
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${CMAKE_SOURCE_DIR}/src/resource/update.sh" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/update.sh"
COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}"
COMMAND bash -c "install_name_tool -change ${LIBUSB_PATH} @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}")
endif()
set_target_properties(CemuBin PROPERTIES

View file

@ -1,12 +1,13 @@
#include "Account.h"
#include "util/helpers/helpers.h"
#include "util/helpers/SystemException.h"
#include "util/helpers/StringHelpers.h"
#include "config/ActiveSettings.h"
#include "Cafe/IOSU/legacy/iosu_crypto.h"
#include "Common/FileStream.h"
#include <boost/random/uniform_int.hpp>
#include <random>
#include <boost/random/uniform_int.hpp>
std::vector<Account> Account::s_account_list;
@ -460,15 +461,14 @@ OnlineValidator Account::ValidateOnlineFiles() const
void Account::ParseFile(class FileStream* file)
{
std::vector<std::string> buffer;
std::string tmp;
while (file->readLine(tmp))
buffer.emplace_back(tmp);
for (const auto& s : buffer)
std::vector<uint8> buffer;
buffer.resize(file->GetSize());
if( file->readData(buffer.data(), buffer.size()) != buffer.size())
throw std::system_error(AccountErrc::ParseError);
for (const auto& s : StringHelpers::StringLineIterator(buffer))
{
std::string_view view = s;
const auto find = view.find(L'=');
const auto find = view.find('=');
if (find == std::string_view::npos)
continue;

View file

@ -16,22 +16,6 @@ enum class OnlineAccountError
kPasswordCacheEmpty,
kNoPrincipalId,
};
template <>
struct fmt::formatter<OnlineAccountError> : formatter<string_view> {
template <typename FormatContext>
auto format(const OnlineAccountError v, FormatContext& ctx) {
switch (v)
{
case OnlineAccountError::kNoAccountId: return formatter<string_view>::format("AccountId missing (The account is not connected to a NNID)", ctx);
case OnlineAccountError::kNoPasswordCached: return formatter<string_view>::format("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)", ctx);
case OnlineAccountError::kPasswordCacheEmpty: return formatter<string_view>::format("AccountPasswordCache is empty (The remember password option on your Wii U must be enabled for this account before dumping it)", ctx);
case OnlineAccountError::kNoPrincipalId: return formatter<string_view>::format("PrincipalId missing", ctx);
default: break;
}
return formatter<string_view>::format("no error", ctx);
}
};
struct OnlineValidator
{

View file

@ -10,6 +10,7 @@ add_library(CemuCafe
Filesystem/fscDeviceRedirect.cpp
Filesystem/fscDeviceWua.cpp
Filesystem/fscDeviceWud.cpp
Filesystem/fscDeviceWuhb.cpp
Filesystem/fsc.h
Filesystem/FST/FST.cpp
Filesystem/FST/FST.h
@ -18,6 +19,9 @@ add_library(CemuCafe
Filesystem/FST/KeyCache.h
Filesystem/WUD/wud.cpp
Filesystem/WUD/wud.h
Filesystem/WUHB/RomFSStructs.h
Filesystem/WUHB/WUHBReader.cpp
Filesystem/WUHB/WUHBReader.h
GamePatch.cpp
GamePatch.h
GameProfile/GameProfile.cpp
@ -40,6 +44,7 @@ add_library(CemuCafe
HW/Espresso/Debugger/DebugSymbolStorage.h
HW/Espresso/Debugger/GDBStub.h
HW/Espresso/Debugger/GDBStub.cpp
HW/Espresso/Debugger/GDBBreakpoints.cpp
HW/Espresso/Debugger/GDBBreakpoints.h
HW/Espresso/EspressoISA.h
HW/Espresso/Interpreter/PPCInterpreterALU.hpp
@ -213,6 +218,8 @@ add_library(CemuCafe
HW/SI/SI.cpp
HW/SI/si.h
HW/VI/VI.cpp
IOSU/ccr_nfc/iosu_ccr_nfc.cpp
IOSU/ccr_nfc/iosu_ccr_nfc.h
IOSU/fsa/fsa_types.h
IOSU/fsa/iosu_fsa.cpp
IOSU/fsa/iosu_fsa.h
@ -240,6 +247,8 @@ add_library(CemuCafe
IOSU/nn/iosu_nn_service.h
IOSU/PDM/iosu_pdm.cpp
IOSU/PDM/iosu_pdm.h
IOSU/ODM/iosu_odm.cpp
IOSU/ODM/iosu_odm.h
OS/common/OSCommon.cpp
OS/common/OSCommon.h
OS/common/OSUtil.h
@ -365,12 +374,24 @@ add_library(CemuCafe
OS/libs/gx2/GX2_Texture.h
OS/libs/gx2/GX2_TilingAperture.cpp
OS/libs/h264_avc/H264Dec.cpp
OS/libs/h264_avc/H264DecBackendAVC.cpp
OS/libs/h264_avc/h264dec.h
OS/libs/h264_avc/H264DecInternal.h
OS/libs/h264_avc/parser
OS/libs/h264_avc/parser/H264Parser.cpp
OS/libs/h264_avc/parser/H264Parser.h
OS/libs/mic/mic.cpp
OS/libs/mic/mic.h
OS/libs/nfc/ndef.cpp
OS/libs/nfc/ndef.h
OS/libs/nfc/nfc.cpp
OS/libs/nfc/nfc.h
OS/libs/nfc/stream.cpp
OS/libs/nfc/stream.h
OS/libs/nfc/TagV0.cpp
OS/libs/nfc/TagV0.h
OS/libs/nfc/TLV.cpp
OS/libs/nfc/TLV.h
OS/libs/nlibcurl/nlibcurl.cpp
OS/libs/nlibcurl/nlibcurlDebug.hpp
OS/libs/nlibcurl/nlibcurl.h
@ -399,6 +420,10 @@ add_library(CemuCafe
OS/libs/nn_idbe/nn_idbe.h
OS/libs/nn_ndm/nn_ndm.cpp
OS/libs/nn_ndm/nn_ndm.h
OS/libs/nn_spm/nn_spm.cpp
OS/libs/nn_spm/nn_spm.h
OS/libs/nn_sl/nn_sl.cpp
OS/libs/nn_sl/nn_sl.h
OS/libs/nn_nfp/AmiiboCrypto.h
OS/libs/nn_nfp/nn_nfp.cpp
OS/libs/nn_nfp/nn_nfp.h
@ -406,6 +431,20 @@ add_library(CemuCafe
OS/libs/nn_nim/nn_nim.h
OS/libs/nn_olv/nn_olv.cpp
OS/libs/nn_olv/nn_olv.h
OS/libs/nn_olv/nn_olv_Common.cpp
OS/libs/nn_olv/nn_olv_Common.h
OS/libs/nn_olv/nn_olv_InitializeTypes.cpp
OS/libs/nn_olv/nn_olv_InitializeTypes.h
OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp
OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.h
OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp
OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h
OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp
OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h
OS/libs/nn_olv/nn_olv_PostTypes.cpp
OS/libs/nn_olv/nn_olv_PostTypes.h
OS/libs/nn_olv/nn_olv_OfflineDB.cpp
OS/libs/nn_olv/nn_olv_OfflineDB.h
OS/libs/nn_pdm/nn_pdm.cpp
OS/libs/nn_pdm/nn_pdm.h
OS/libs/nn_save/nn_save.cpp
@ -416,10 +455,26 @@ add_library(CemuCafe
OS/libs/nn_uds/nn_uds.h
OS/libs/nsyshid/nsyshid.cpp
OS/libs/nsyshid/nsyshid.h
OS/libs/nsyshid/Backend.h
OS/libs/nsyshid/AttachDefaultBackends.cpp
OS/libs/nsyshid/Whitelist.cpp
OS/libs/nsyshid/Whitelist.h
OS/libs/nsyshid/BackendEmulated.cpp
OS/libs/nsyshid/BackendEmulated.h
OS/libs/nsyshid/BackendLibusb.cpp
OS/libs/nsyshid/BackendLibusb.h
OS/libs/nsyshid/Dimensions.cpp
OS/libs/nsyshid/Dimensions.h
OS/libs/nsyshid/Infinity.cpp
OS/libs/nsyshid/Infinity.h
OS/libs/nsyshid/Skylander.cpp
OS/libs/nsyshid/Skylander.h
OS/libs/nsyskbd/nsyskbd.cpp
OS/libs/nsyskbd/nsyskbd.h
OS/libs/nsysnet/nsysnet.cpp
OS/libs/nsysnet/nsysnet.h
OS/libs/ntag/ntag.cpp
OS/libs/ntag/ntag.h
OS/libs/padscore/padscore.cpp
OS/libs/padscore/padscore.h
OS/libs/proc_ui/proc_ui.cpp
@ -454,8 +509,6 @@ add_library(CemuCafe
OS/RPL/rpl_structs.h
OS/RPL/rpl_symbol_storage.cpp
OS/RPL/rpl_symbol_storage.h
TitleList/BaseInfo.cpp
TitleList/BaseInfo.h
TitleList/GameInfo.h
TitleList/ParsedMetaXml.h
TitleList/SaveInfo.cpp
@ -477,6 +530,12 @@ set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CON
target_include_directories(CemuCafe PUBLIC "../")
if (glslang_VERSION VERSION_LESS "15.0.0")
set(glslang_target "glslang::SPIRV")
else()
set(glslang_target "glslang")
endif()
target_link_libraries(CemuCafe PRIVATE
CemuAsm
CemuAudio
@ -492,7 +551,7 @@ target_link_libraries(CemuCafe PRIVATE
Boost::nowide
CURL::libcurl
fmt::fmt
glslang::SPIRV
${glslang_target}
ih264d
OpenSSL::Crypto
OpenSSL::SSL
@ -505,9 +564,21 @@ target_link_libraries(CemuCafe PRIVATE
if (ENABLE_WAYLAND)
# PUBLIC because wayland-client.h is included in VulkanAPI.h
target_link_libraries(CemuCafe PUBLIC Wayland::client)
target_link_libraries(CemuCafe PUBLIC Wayland::Client)
endif()
if (ENABLE_VCPKG)
if(WIN32)
set(PKG_CONFIG_EXECUTABLE "${VCPKG_INSTALLED_DIR}/x64-windows/tools/pkgconf/pkgconf.exe")
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
target_link_libraries(CemuCafe PRIVATE PkgConfig::libusb)
else ()
find_package(libusb MODULE REQUIRED)
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
endif ()
if (ENABLE_WXWIDGETS)
target_link_libraries(CemuCafe PRIVATE wx::base wx::core)
endif()

View file

@ -4,17 +4,17 @@
#include "Cafe/GameProfile/GameProfile.h"
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
#include "audio/IAudioAPI.h"
#include "audio/IAudioInputAPI.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "config/ActiveSettings.h"
#include "config/LaunchSettings.h"
#include "Cafe/TitleList/GameInfo.h"
#include "util/helpers/SystemException.h"
#include "Cafe/GraphicPack/GraphicPack2.h"
#include "util/helpers/SystemException.h"
#include "Common/cpu_features.h"
#include "input/InputManager.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/TitleList/TitleList.h"
#include "Cafe/TitleList/GameInfo.h"
@ -22,14 +22,9 @@
#include "Cafe/OS/libs/snd_core/ax.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/HW/Latte/Core/Latte.h"
#include "Cafe/Filesystem/FST/FST.h"
#include "Common/FileStream.h"
#include "GamePatch.h"
#include <time.h>
#include "HW/Espresso/Debugger/GDBStub.h"
#include "Cafe/IOSU/legacy/iosu_ioctl.h"
@ -41,12 +36,14 @@
#include "Cafe/IOSU/legacy/iosu_boss.h"
#include "Cafe/IOSU/legacy/iosu_nim.h"
#include "Cafe/IOSU/PDM/iosu_pdm.h"
#include "Cafe/IOSU/ccr_nfc/iosu_ccr_nfc.h"
// IOSU initializer functions
#include "Cafe/IOSU/kernel/iosu_kernel.h"
#include "Cafe/IOSU/fsa/iosu_fsa.h"
#include "Cafe/IOSU/ODM/iosu_odm.h"
// Cafe OS initializer functions
// Cafe OS initializer and shutdown functions
#include "Cafe/OS/libs/avm/avm.h"
#include "Cafe/OS/libs/drmapp/drmapp.h"
#include "Cafe/OS/libs/TCL/TCL.h"
@ -56,11 +53,14 @@
#include "Cafe/OS/libs/gx2/GX2.h"
#include "Cafe/OS/libs/gx2/GX2_Misc.h"
#include "Cafe/OS/libs/mic/mic.h"
#include "Cafe/OS/libs/nfc/nfc.h"
#include "Cafe/OS/libs/ntag/ntag.h"
#include "Cafe/OS/libs/nn_aoc/nn_aoc.h"
#include "Cafe/OS/libs/nn_pdm/nn_pdm.h"
#include "Cafe/OS/libs/nn_cmpt/nn_cmpt.h"
#include "Cafe/OS/libs/nn_ccr/nn_ccr.h"
#include "Cafe/OS/libs/nn_temp/nn_temp.h"
#include "Cafe/OS/libs/nn_save/nn_save.h"
// HW interfaces
#include "Cafe/HW/SI/si.h"
@ -68,6 +68,15 @@
// dependency to be removed
#include "gui/guiWrapper.h"
#include <time.h>
#if BOOST_OS_LINUX
#include <sys/sysinfo.h>
#elif BOOST_OS_MACOS
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
std::string _pathToExecutable;
std::string _pathToBaseExecutable;
@ -160,7 +169,7 @@ void LoadMainExecutable()
{
// RPX
RPLLoader_AddDependency(_pathToExecutable.c_str());
applicationRPX = rpl_loadFromMem(rpxData, rpxSize, (char*)_pathToExecutable.c_str());
applicationRPX = RPLLoader_LoadFromMemory(rpxData, rpxSize, (char*)_pathToExecutable.c_str());
if (!applicationRPX)
{
wxMessageBox(_("Failed to run this title because the executable is damaged"));
@ -211,7 +220,7 @@ void InfoLog_TitleLoaded()
fs::path effectiveSavePath = getTitleSavePath();
std::error_code ec;
const bool saveDirExists = fs::exists(effectiveSavePath, ec);
cemuLog_force("Save path: {}{}", _pathToUtf8(effectiveSavePath), saveDirExists ? "" : " (not present)");
cemuLog_log(LogType::Force, "Save path: {}{}", _pathToUtf8(effectiveSavePath), saveDirExists ? "" : " (not present)");
// log shader cache name
cemuLog_log(LogType::Force, "Shader cache file: shaderCache/transferable/{:016x}.bin", titleId);
@ -246,14 +255,7 @@ void InfoLog_PrintActiveSettings()
if(!GetConfig().vk_accurate_barriers.GetValue())
cemuLog_log(LogType::Force, "Accurate barriers are disabled!");
}
cemuLog_log(LogType::Force, "Console language: {}", config.console_language);
}
void PPCCore_setupSPR(PPCInterpreter_t* hCPU, uint32 coreIndex)
{
hCPU->sprExtended.PVR = 0x70010001;
hCPU->spr.UPIR = coreIndex;
hCPU->sprExtended.msr |= MSR_FP; // enable floating point
cemuLog_log(LogType::Force, "Console language: {}", stdx::to_underlying(config.console_language.GetValue()));
}
struct SharedDataEntry
@ -284,7 +286,7 @@ struct
static_assert(sizeof(SharedDataEntry) == 0x1C);
uint32 loadSharedData()
uint32 LoadSharedData()
{
// check if font files are dumped
bool hasAllShareddataFiles = true;
@ -395,7 +397,7 @@ void cemu_initForGame()
// replace any known function signatures with our HLE implementations and patch bugs in the games
GamePatch_scan();
}
LatteGPUState.alwaysDisplayDRC = ActiveSettings::DisplayDRCEnabled();
LatteGPUState.isDRCPrimary = ActiveSettings::DisplayDRCEnabled();
InfoLog_PrintActiveSettings();
Latte_Start();
// check for debugger entrypoint bp
@ -421,61 +423,162 @@ void cemu_initForGame()
coreinit::OSRunThread(initialThread, PPCInterpreter_makeCallableExportDepr(coreinit_start), 0, nullptr);
// init AX and start AX I/O thread
snd_core::AXOut_init();
// init ppc recompiler
PPCRecompiler_init();
}
void cemu_deinitForGame()
{
// reset audio
snd_core::AXOut_reset();
snd_core::reset();
// reset alarms
coreinit::OSAlarm_resetAll();
// delete all threads
PPCCore_deleteAllThreads();
// reset mount paths
fsc_unmountAll();
// reset RPL loader
RPLLoader_ResetState();
// reset GX2
GX2::_GX2DriverReset();
}
namespace CafeSystem
{
void InitVirtualMlcStorage();
void MlcStorageMountTitle(TitleInfo& titleInfo);
void MlcStorageUnmountAllTitles();
bool sLaunchModeIsStandalone = false;
static bool s_initialized = false;
static SystemImplementation* s_implementation{nullptr};
bool sLaunchModeIsStandalone = false;
std::optional<std::vector<std::string>> s_overrideArgs;
bool sSystemRunning = false;
TitleId sForegroundTitleId = 0;
GameInfo2 sGameInfo_ForegroundTitle;
static void _CheckForWine()
{
#if BOOST_OS_WINDOWS
const HMODULE hmodule = GetModuleHandleA("ntdll.dll");
if (!hmodule)
return;
const auto pwine_get_version = (const char*(__cdecl*)())GetProcAddress(hmodule, "wine_get_version");
if (pwine_get_version)
{
cemuLog_log(LogType::Force, "Wine version: {}", pwine_get_version());
}
#endif
}
void logCPUAndMemoryInfo()
{
std::string cpuName = g_CPUFeatures.GetCPUName();
if (!cpuName.empty())
cemuLog_log(LogType::Force, "CPU: {}", cpuName);
#if BOOST_OS_WINDOWS
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
GlobalMemoryStatusEx(&statex);
uint32 memoryInMB = (uint32)(statex.ullTotalPhys / 1024LL / 1024LL);
cemuLog_log(LogType::Force, "RAM: {}MB", memoryInMB);
#elif BOOST_OS_LINUX
struct sysinfo info {};
sysinfo(&info);
cemuLog_log(LogType::Force, "RAM: {}MB", ((static_cast<uint64_t>(info.totalram) * info.mem_unit) / 1024LL / 1024LL));
#elif BOOST_OS_MACOS
int64_t totalRam;
size_t size = sizeof(totalRam);
int result = sysctlbyname("hw.memsize", &totalRam, &size, NULL, 0);
if (result == 0)
cemuLog_log(LogType::Force, "RAM: {}MB", (totalRam / 1024LL / 1024LL));
#endif
}
#if BOOST_OS_WINDOWS
std::string GetWindowsNamedVersion(uint32& buildNumber)
{
char productName[256];
HKEY hKey;
DWORD dwType = REG_SZ;
DWORD dwSize = sizeof(productName);
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
{
if (RegQueryValueExA(hKey, "ProductName", NULL, &dwType, (LPBYTE)productName, &dwSize) != ERROR_SUCCESS)
strcpy(productName, "Windows");
RegCloseKey(hKey);
}
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
buildNumber = osvi.dwBuildNumber;
return std::string(productName);
}
#endif
void logPlatformInfo()
{
std::string buffer;
const char* platform = NULL;
#if BOOST_OS_WINDOWS
uint32 buildNumber;
std::string windowsVersionName = GetWindowsNamedVersion(buildNumber);
buffer = fmt::format("{} (Build {})", windowsVersionName, buildNumber);
platform = buffer.c_str();
#elif BOOST_OS_LINUX
if (getenv ("APPIMAGE"))
platform = "Linux (AppImage)";
else if (getenv ("SNAP"))
platform = "Linux (Snap)";
else if (platform = getenv ("container"))
{
if (strcmp (platform, "flatpak") == 0)
platform = "Linux (Flatpak)";
}
else
platform = "Linux";
#elif BOOST_OS_MACOS
platform = "MacOS";
#endif
cemuLog_log(LogType::Force, "Platform: {}", platform);
}
static std::vector<IOSUModule*> s_iosuModules =
{
// entries in this list are ordered by initialization order. Shutdown in reverse order
iosu::kernel::GetModule(),
iosu::acp::GetModule(),
iosu::fpd::GetModule(),
iosu::pdm::GetModule(),
iosu::ccr_nfc::GetModule(),
};
// initialize all subsystems which are persistent and don't depend on a game running
void Initialize()
{
static bool s_initialized = false;
if (s_initialized)
return;
s_initialized = true;
// init core systems
cemuLog_log(LogType::Force, "------- Init {} -------", BUILD_VERSION_WITH_NAME_STRING);
fsc_init();
memory_init();
cemuLog_log(LogType::Force, "Init Wii U memory space (base: 0x{:016x})", (size_t)memory_base);
PPCCore_init();
RPLLoader_InitState();
cemuLog_log(LogType::Force, "mlc01 path: {}", _pathToUtf8(ActiveSettings::GetMlcPath()));
_CheckForWine();
// CPU and RAM info
logCPUAndMemoryInfo();
logPlatformInfo();
cemuLog_log(LogType::Force, "Used CPU extensions: {}", g_CPUFeatures.GetCommaSeparatedExtensionList());
// misc systems
rplSymbolStorage_init();
// allocate memory for all SysAllocators
// must happen before all COS modules, but also before iosu::kernel::Init()
// must happen before COS module init, but also before iosu::kernel::Initialize()
SysAllocatorContainer::GetInstance().Initialize();
// init IOSU
iosu::kernel::Initialize();
// init IOSU modules
for(auto& module : s_iosuModules)
module->SystemLaunch();
// init IOSU (deprecated manual init)
iosuCrypto_init();
iosu::fsa::Initialize();
iosuIoctl_init();
iosuAct_init_depr();
iosu::act::Initialize();
iosu::fpd::Initialize();
iosu::iosuMcp_init();
iosu::mcp::Init();
iosu::iosuAcp_init();
iosu::boss_init();
iosu::nim::Initialize();
iosu::pdm::Initialize();
iosu::odm::Initialize();
// init Cafe OS
avm::Initialize();
drmapp::Initialize();
@ -489,49 +592,86 @@ namespace CafeSystem
H264::Initialize();
snd_core::Initialize();
mic::Initialize();
nfc::Initialize();
ntag::Initialize();
// init hardware register interfaces
HW_SI::Initialize();
}
void SetImplementation(SystemImplementation* impl)
{
s_implementation = impl;
}
void Shutdown()
{
cemu_assert_debug(s_initialized);
// if a title is running, shut it down
if (sSystemRunning)
ShutdownTitle();
// shutdown persistent subsystems (deprecated manual shutdown)
iosu::odm::Shutdown();
iosu::act::Stop();
iosu::mcp::Shutdown();
iosu::fsa::Shutdown();
// shutdown IOSU modules
for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it)
(*it)->SystemExit();
s_initialized = false;
}
std::string GetInternalVirtualCodeFolder()
{
return "/internal/current_title/code/";
}
STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId)
void MountBaseDirectories()
{
const auto mlc = ActiveSettings::GetMlcPath();
FSCDeviceHostFS_Mount("/cemuBossStorage/", _pathToUtf8(mlc / "usr/boss/"), FSC_PRIORITY_BASE);
FSCDeviceHostFS_Mount("/vol/storage_mlc01/", _pathToUtf8(mlc / ""), FSC_PRIORITY_BASE);
}
void UnmountBaseDirectories()
{
fsc_unmount("/vol/storage_mlc01/", FSC_PRIORITY_BASE);
fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE);
}
PREPARE_STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId)
{
cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId);
cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId);
sGameInfo_ForegroundTitle = CafeTitleList::GetGameInfo(titleId);
if (!sGameInfo_ForegroundTitle.IsValid())
{
cemuLog_log(LogType::Force, "Mounting failed: Game meta information is either missing, inaccessible or not valid (missing or invalid .xml files in code and meta folder)");
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
}
// check base
TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase();
if (!titleBase.IsValid())
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
if(!titleBase.ParseXmlInfo())
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
cemuLog_log(LogType::Force, "Base: {}", titleBase.GetPrintPath());
// mount base
if (!titleBase.Mount("/vol/content", "content", FSC_PRIORITY_BASE) || !titleBase.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_BASE))
{
cemuLog_log(LogType::Force, "Mounting failed");
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
}
// check update
TitleInfo& titleUpdate = sGameInfo_ForegroundTitle.GetUpdate();
if (titleUpdate.IsValid())
{
if (!titleUpdate.ParseXmlInfo())
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
cemuLog_log(LogType::Force, "Update: {}", titleUpdate.GetPrintPath());
// mount update
if (!titleUpdate.Mount("/vol/content", "content", FSC_PRIORITY_PATCH) || !titleUpdate.Mount(GetInternalVirtualCodeFolder(), "code", FSC_PRIORITY_PATCH))
{
cemuLog_log(LogType::Force, "Mounting failed");
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
}
}
else
@ -543,26 +683,49 @@ namespace CafeSystem
// todo - support for multi-title AOC
TitleInfo& titleAOC = aocList[0];
if (!titleAOC.ParseXmlInfo())
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
cemu_assert_debug(titleAOC.IsValid());
cemuLog_log(LogType::Force, "DLC: {}", titleAOC.GetPrintPath());
// mount AOC
if (!titleAOC.Mount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId()), "content", FSC_PRIORITY_PATCH))
{
cemuLog_log(LogType::Force, "Mounting failed");
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
}
}
else
cemuLog_log(LogType::Force, "DLC: Not present");
sForegroundTitleId = titleId;
return STATUS_CODE::SUCCESS;
return PREPARE_STATUS_CODE::SUCCESS;
}
STATUS_CODE SetupExecutable()
void UnmountForegroundTitle()
{
if(sLaunchModeIsStandalone)
return;
cemu_assert_debug(sGameInfo_ForegroundTitle.IsValid()); // unmounting title which was never mounted?
if (!sGameInfo_ForegroundTitle.IsValid())
return;
sGameInfo_ForegroundTitle.GetBase().Unmount("/vol/content");
sGameInfo_ForegroundTitle.GetBase().Unmount(GetInternalVirtualCodeFolder());
if (sGameInfo_ForegroundTitle.HasUpdate())
{
if(auto& update = sGameInfo_ForegroundTitle.GetUpdate(); update.IsValid())
{
update.Unmount("/vol/content");
update.Unmount(GetInternalVirtualCodeFolder());
}
}
auto aocList = sGameInfo_ForegroundTitle.GetAOC();
if (!aocList.empty())
{
TitleInfo& titleAOC = aocList[0];
titleAOC.Unmount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId()));
}
}
PREPARE_STATUS_CODE SetupExecutable()
{
// mount mlc directories
fscDeviceHostFS_mapBaseDirectories_deprecated();
// set rpx path from cos.xml if available
_pathToBaseExecutable = _pathToExecutable;
if (!sLaunchModeIsStandalone)
@ -593,35 +756,46 @@ namespace CafeSystem
}
}
LoadMainExecutable();
gameProfile_load();
return STATUS_CODE::SUCCESS;
return PREPARE_STATUS_CODE::SUCCESS;
}
STATUS_CODE PrepareForegroundTitle(TitleId titleId)
void SetupMemorySpace()
{
memory_mapForCurrentTitle();
LoadSharedData();
}
void DestroyMemorySpace()
{
memory_unmapForCurrentTitle();
}
PREPARE_STATUS_CODE PrepareForegroundTitle(TitleId titleId)
{
CafeTitleList::WaitForMandatoryScan();
sLaunchModeIsStandalone = false;
_pathToExecutable.clear();
TitleIdParser tip(titleId);
if (tip.GetType() == TitleIdParser::TITLE_TYPE::AOC || tip.GetType() == TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE)
cemuLog_log(LogType::Force, "Launched titleId is not the base of a title");
// mount title folders
STATUS_CODE r = LoadAndMountForegroundTitle(titleId);
if (r != STATUS_CODE::SUCCESS)
// mount mlc storage
MountBaseDirectories();
// mount title folders
PREPARE_STATUS_CODE r = LoadAndMountForegroundTitle(titleId);
if (r != PREPARE_STATUS_CODE::SUCCESS)
return r;
// map memory
memory_mapForCurrentTitle();
// load RPX
r = SetupExecutable();
if (r != STATUS_CODE::SUCCESS)
gameProfile_load();
// setup memory space and PPC recompiler
SetupMemorySpace();
PPCRecompiler_init();
r = SetupExecutable(); // load RPX
if (r != PREPARE_STATUS_CODE::SUCCESS)
return r;
loadSharedData();
InitVirtualMlcStorage();
return STATUS_CODE::SUCCESS;
return PREPARE_STATUS_CODE::SUCCESS;
}
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path)
PREPARE_STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path)
{
sLaunchModeIsStandalone = true;
cemuLog_log(LogType::Force, "Launching executable in standalone mode due to incorrect layout or missing meta files");
@ -639,7 +813,7 @@ namespace CafeSystem
if (!r)
{
cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath));
return STATUS_CODE::UNABLE_TO_MOUNT;
return PREPARE_STATUS_CODE::UNABLE_TO_MOUNT;
}
}
}
@ -651,28 +825,29 @@ namespace CafeSystem
// since a lot of systems (including save folder location) rely on a TitleId, we derive a placeholder id from the executable hash
auto execData = fsc_extractFile(_pathToExecutable.c_str());
if (!execData)
return STATUS_CODE::INVALID_RPX;
return PREPARE_STATUS_CODE::INVALID_RPX;
uint32 h = generateHashFromRawRPXData(execData->data(), execData->size());
sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h;
cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId);
// load executable
memory_mapForCurrentTitle();
SetupExecutable();
loadSharedData();
// setup memory space and ppc recompiler
SetupMemorySpace();
PPCRecompiler_init();
// load executable
SetupExecutable();
InitVirtualMlcStorage();
return STATUS_CODE::SUCCESS;
return PREPARE_STATUS_CODE::SUCCESS;
}
void _LaunchTitleThread()
{
// init
for(auto& module : s_iosuModules)
module->TitleStart();
cemu_initForGame();
// enter scheduler
if (ActiveSettings::GetCPUMode() == CPUMode::MulticoreRecompiler)
if (ActiveSettings::GetCPUMode() == CPUMode::MulticoreRecompiler && !LaunchSettings::ForceInterpreter())
coreinit::OSSchedulerBegin(3);
else
coreinit::OSSchedulerBegin(1);
iosu::pdm::StartTrackingTime(GetForegroundTitleId());
}
void LaunchForegroundTitle()
@ -703,6 +878,13 @@ namespace CafeSystem
return sGameInfo_ForegroundTitle.GetVersion();
}
uint32 GetForegroundTitleSDKVersion()
{
if (sLaunchModeIsStandalone)
return 999999;
return sGameInfo_ForegroundTitle.GetSDKVersion();
}
CafeConsoleRegion GetForegroundTitleRegion()
{
if (sLaunchModeIsStandalone)
@ -723,6 +905,13 @@ namespace CafeSystem
return applicationName;
}
uint32 GetForegroundTitleOlvAccesskey()
{
if (sLaunchModeIsStandalone)
return -1;
return sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetOlvAccesskey();
}
std::string GetForegroundTitleArgStr()
{
if (sLaunchModeIsStandalone)
@ -733,6 +922,47 @@ namespace CafeSystem
return sGameInfo_ForegroundTitle.GetBase().GetArgStr();
}
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group)
{
if (sLaunchModeIsStandalone)
return CosCapabilityBits::All;
auto& update = sGameInfo_ForegroundTitle.GetUpdate();
if (update.IsValid())
{
ParsedCosXml* cosXml = update.GetCosInfo();
if (cosXml)
return cosXml->GetCapabilityBits(group);
}
auto& base = sGameInfo_ForegroundTitle.GetBase();
if(base.IsValid())
{
ParsedCosXml* cosXml = base.GetCosInfo();
if (cosXml)
return cosXml->GetCapabilityBits(group);
}
return CosCapabilityBits::All;
}
// when switching titles custom parameters can be passed, returns true if override args are used
bool GetOverrideArgStr(std::vector<std::string>& args)
{
args.clear();
if(!s_overrideArgs)
return false;
args = *s_overrideArgs;
return true;
}
void SetOverrideArgs(std::span<std::string> args)
{
s_overrideArgs = std::vector<std::string>(args.begin(), args.end());
}
void UnsetOverrideArgs()
{
s_overrideArgs = std::nullopt;
}
// pick platform region based on title region
CafeConsoleRegion GetPlatformRegion()
{
@ -749,39 +979,32 @@ namespace CafeSystem
void UnmountCurrentTitle()
{
TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase();
if (titleBase.IsValid())
titleBase.UnmountAll();
if (sGameInfo_ForegroundTitle.HasUpdate())
{
TitleInfo& titleUpdate = sGameInfo_ForegroundTitle.GetUpdate();
if (titleUpdate.IsValid())
titleUpdate.UnmountAll();
}
if (sGameInfo_ForegroundTitle.HasAOC())
{
auto titleInfoList = sGameInfo_ForegroundTitle.GetAOC();
for(auto& it : titleInfoList)
{
if (it.IsValid())
it.UnmountAll();
}
}
fsc_unmount("/internal/code/", FSC_PRIORITY_BASE);
UnmountForegroundTitle();
fsc_unmount("/internal/code/", FSC_PRIORITY_BASE);
}
void ShutdownTitle()
{
if(!sSystemRunning)
return;
coreinit::OSSchedulerEnd();
Latte_Stop();
iosu::pdm::Stop();
iosu::act::Stop();
iosu::mcp::Shutdown();
iosu::fsa::Shutdown();
GraphicPack2::Reset();
UnmountCurrentTitle();
coreinit::OSSchedulerEnd();
Latte_Stop();
// reset Cafe OS userspace modules
snd_core::reset();
coreinit::OSAlarm_Shutdown();
GX2::_GX2DriverReset();
nn::save::ResetToDefaultState();
coreinit::__OSDeleteAllActivePPCThreads();
RPLLoader_ResetState();
for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it)
(*it)->TitleStop();
// reset Cemu subsystems
PPCRecompiler_Shutdown();
GraphicPack2::Reset();
UnmountCurrentTitle();
MlcStorageUnmountAllTitles();
UnmountBaseDirectories();
DestroyMemorySpace();
sSystemRunning = false;
}
@ -831,10 +1054,7 @@ namespace CafeSystem
}
TitleId titleId = titleInfo.GetAppTitleId();
if (m_mlcMountedTitles.find(titleId) != m_mlcMountedTitles.end())
{
cemu_assert_suspicious(); // already mounted
return;
}
std::string mlcStoragePath = GetMlcStoragePath(titleId);
TitleInfo* mountTitleInfo = new TitleInfo(titleInfo);
if (!mountTitleInfo->Mount(mlcStoragePath, "", FSC_PRIORITY_BASE))
@ -861,6 +1081,16 @@ namespace CafeSystem
MlcStorageMountTitle(it);
}
void MlcStorageUnmountAllTitles()
{
for(auto& it : m_mlcMountedTitles)
{
std::string mlcStoragePath = GetMlcStoragePath(it.first);
it.second->Unmount(mlcStoragePath);
}
m_mlcMountedTitles.clear();
}
uint32 GetRPXHashBase()
{
return currentBaseApplicationHash;
@ -871,4 +1101,9 @@ namespace CafeSystem
return currentUpdatedApplicationHash;
}
void RequestRecreateCanvas()
{
s_implementation->CafeRecreateCanvas();
}
}

View file

@ -4,28 +4,46 @@
#include "Cafe/TitleList/TitleId.h"
#include "config/CemuConfig.h"
enum class CosCapabilityBits : uint64;
enum class CosCapabilityGroup : uint32;
namespace CafeSystem
{
enum class STATUS_CODE
class SystemImplementation
{
public:
virtual void CafeRecreateCanvas() = 0;
};
enum class PREPARE_STATUS_CODE
{
SUCCESS,
INVALID_RPX,
UNABLE_TO_MOUNT, // failed to mount through TitleInfo (most likely caused by an invalid or outdated path)
//BAD_META_DATA, - the title list only stores titles with valid meta, so this error code is impossible
};
void Initialize();
STATUS_CODE PrepareForegroundTitle(TitleId titleId);
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path);
void SetImplementation(SystemImplementation* impl);
void Shutdown();
PREPARE_STATUS_CODE PrepareForegroundTitle(TitleId titleId);
PREPARE_STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path);
void LaunchForegroundTitle();
bool IsTitleRunning();
bool GetOverrideArgStr(std::vector<std::string>& args);
void SetOverrideArgs(std::span<std::string> args);
void UnsetOverrideArgs();
TitleId GetForegroundTitleId();
uint16 GetForegroundTitleVersion();
uint32 GetForegroundTitleSDKVersion();
CafeConsoleRegion GetForegroundTitleRegion();
CafeConsoleRegion GetPlatformRegion();
std::string GetForegroundTitleName();
std::string GetForegroundTitleArgStr();
uint32 GetForegroundTitleOlvAccesskey();
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group);
void ShutdownTitle();
@ -36,6 +54,8 @@ namespace CafeSystem
uint32 GetRPXHashBase();
uint32 GetRPXHashUpdated();
void RequestRecreateCanvas();
};
extern RPLModule* applicationRPX;

View file

@ -3,8 +3,7 @@
#include "Cemu/ncrypto/ncrypto.h"
#include "Cafe/Filesystem/WUD/wud.h"
#include "util/crypto/aes128.h"
#include "openssl/evp.h" /* EVP_Digest */
#include "openssl/sha.h" /* SHA1 / SHA256_DIGEST_LENGTH */
#include "openssl/sha.h" /* SHA1 / SHA256 */
#include "fstUtil.h"
#include "FST.h"
@ -12,6 +11,8 @@
#include "boost/range/adaptor/reversed.hpp"
#define SET_FST_ERROR(__code) if (errorCodeOut) *errorCodeOut = ErrorCode::__code
class FSTDataSource
{
public:
@ -139,7 +140,7 @@ struct DiscPartitionTableHeader
static constexpr uint32 MAGIC_VALUE = 0xCCA6E67B;
/* +0x00 */ uint32be magic;
/* +0x04 */ uint32be sectorSize; // must be 0x8000?
/* +0x04 */ uint32be blockSize; // must be 0x8000?
/* +0x08 */ uint8 partitionTableHash[20]; // hash of the data range at +0x800 to end of sector (0x8000)
/* +0x1C */ uint32be numPartitions;
};
@ -162,10 +163,10 @@ struct DiscPartitionHeader
static constexpr uint32 MAGIC_VALUE = 0xCC93A4F5;
/* +0x00 */ uint32be magic;
/* +0x04 */ uint32be sectorSize; // must match DISC_SECTOR_SIZE
/* +0x04 */ uint32be sectorSize; // must match DISC_SECTOR_SIZE for hashed blocks
/* +0x08 */ uint32be ukn008;
/* +0x0C */ uint32be ukn00C;
/* +0x0C */ uint32be ukn00C; // h3 array size?
/* +0x10 */ uint32be h3HashNum;
/* +0x14 */ uint32be fstSize; // in bytes
/* +0x18 */ uint32be fstSector; // relative to partition start
@ -176,13 +177,15 @@ struct DiscPartitionHeader
/* +0x24 */ uint8 fstHashType;
/* +0x25 */ uint8 fstEncryptionType; // purpose of this isn't really understood. Maybe it controls which key is being used? (1 -> disc key, 2 -> partition key)
/* +0x26 */ uint8 versionA;
/* +0x27 */ uint8 ukn027; // also a version field?
/* +0x26 */ uint8be versionA;
/* +0x27 */ uint8be ukn027; // also a version field?
// there is an array at +0x40 ? Related to H3 list. Also related to value at +0x0C and h3HashNum
/* +0x28 */ uint8be _uknOrPadding028[0x18];
/* +0x40 */ uint8be h3HashArray[32]; // dynamic size. Only present if fstHashType != 0
};
static_assert(sizeof(DiscPartitionHeader) == 0x28);
static_assert(sizeof(DiscPartitionHeader) == 0x40+0x20);
bool FSTVolume::FindDiscKey(const fs::path& path, NCrypto::AesKey& discTitleKey)
{
@ -215,23 +218,22 @@ bool FSTVolume::FindDiscKey(const fs::path& path, NCrypto::AesKey& discTitleKey)
// open WUD image using key cache
// if no matching key is found then keyFound will return false
FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, bool* keyFound)
FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, ErrorCode* errorCodeOut)
{
SET_FST_ERROR(UNKNOWN_ERROR);
KeyCache_Prepare();
NCrypto::AesKey discTitleKey;
if (!FindDiscKey(path, discTitleKey))
{
if(keyFound)
*keyFound = false;
SET_FST_ERROR(DISC_KEY_MISSING);
return nullptr;
}
if(keyFound)
*keyFound = true;
return OpenFromDiscImage(path, discTitleKey);
return OpenFromDiscImage(path, discTitleKey, errorCodeOut);
}
// open WUD image
FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& discTitleKey)
FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& discTitleKey, ErrorCode* errorCodeOut)
{
// WUD images support multiple partitions, each with their own key and FST
// the process for loading game data FSTVolume from a WUD image is as follows:
@ -240,6 +242,7 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
// 3) find main GM partition
// 4) use SI information to get titleKey for GM partition
// 5) Load FST for GM
SET_FST_ERROR(UNKNOWN_ERROR);
std::unique_ptr<FSTDataSourceWUD> dataSource(FSTDataSourceWUD::Open(path));
if (!dataSource)
return nullptr;
@ -267,7 +270,7 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
cemuLog_log(LogType::Force, "Disc image rejected because decryption failed");
return nullptr;
}
if (partitionHeader->sectorSize != DISC_SECTOR_SIZE)
if (partitionHeader->blockSize != DISC_SECTOR_SIZE)
{
cemuLog_log(LogType::Force, "Disc image rejected because partition sector size is invalid");
return nullptr;
@ -334,6 +337,9 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
cemu_assert_debug(partitionHeaderSI.fstEncryptionType == 1);
// todo - check other fields?
if(partitionHeaderSI.fstHashType == 0 && partitionHeaderSI.h3HashNum != 0)
cemuLog_log(LogType::Force, "FST: Partition uses unhashed blocks but stores a non-zero amount of H3 hashes");
// GM partition
DiscPartitionHeader partitionHeaderGM{};
if (!readPartitionHeader(partitionHeaderGM, gmPartitionIndex))
@ -347,9 +353,10 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
// if decryption is necessary
// load SI FST
dataSource->SetBaseOffset((uint64)partitionArray[siPartitionIndex].partitionAddress * DISC_SECTOR_SIZE);
auto siFST = OpenFST(dataSource.get(), (uint64)partitionHeaderSI.fstSector * DISC_SECTOR_SIZE, partitionHeaderSI.fstSize, &discTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderSI.fstHashType));
auto siFST = OpenFST(dataSource.get(), (uint64)partitionHeaderSI.fstSector * DISC_SECTOR_SIZE, partitionHeaderSI.fstSize, &discTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderSI.fstHashType), nullptr);
if (!siFST)
return nullptr;
cemu_assert_debug(!(siFST->HashIsDisabled() && partitionHeaderSI.h3HashNum != 0)); // if hash is disabled, no H3 data may be present
// load ticket file for partition that we want to decrypt
NCrypto::ETicketParser ticketParser;
std::vector<uint8> ticketData = siFST->ExtractFile(fmt::format("{:02x}/title.tik", gmPartitionIndex));
@ -358,18 +365,38 @@ FSTVolume* FSTVolume::OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& d
cemuLog_log(LogType::Force, "Disc image ticket file is invalid");
return nullptr;
}
#if 0
// each SI partition seems to contain a title.tmd that we could parse and which should have information about the associated GM partition
// but the console seems to ignore this file for disc images, at least when mounting, so we shouldn't rely on it either
std::vector<uint8> tmdData = siFST->ExtractFile(fmt::format("{:02x}/title.tmd", gmPartitionIndex));
if (tmdData.empty())
{
cemuLog_log(LogType::Force, "Disc image TMD file is missing");
return nullptr;
}
// parse TMD
NCrypto::TMDParser tmdParser;
if (!tmdParser.parse(tmdData.data(), tmdData.size()))
{
cemuLog_log(LogType::Force, "Disc image TMD file is invalid");
return nullptr;
}
#endif
delete siFST;
NCrypto::AesKey gmTitleKey;
ticketParser.GetTitleKey(gmTitleKey);
// load GM partition
dataSource->SetBaseOffset((uint64)partitionArray[gmPartitionIndex].partitionAddress * DISC_SECTOR_SIZE);
return OpenFST(std::move(dataSource), (uint64)partitionHeaderGM.fstSector * DISC_SECTOR_SIZE, partitionHeaderGM.fstSize, &gmTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderGM.fstHashType));
FSTVolume* r = OpenFST(std::move(dataSource), (uint64)partitionHeaderGM.fstSector * DISC_SECTOR_SIZE, partitionHeaderGM.fstSize, &gmTitleKey, static_cast<FSTVolume::ClusterHashMode>(partitionHeaderGM.fstHashType), nullptr);
if (r)
SET_FST_ERROR(OK);
cemu_assert_debug(!(r->HashIsDisabled() && partitionHeaderGM.h3HashNum != 0)); // if hash is disabled, no H3 data may be present
return r;
}
FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath)
FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath, ErrorCode* errorCodeOut)
{
SET_FST_ERROR(UNKNOWN_ERROR);
// load TMD
FileStream* tmdFile = FileStream::openFile2(folderPath / "title.tmd");
if (!tmdFile)
@ -379,17 +406,26 @@ FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath)
delete tmdFile;
NCrypto::TMDParser tmdParser;
if (!tmdParser.parse(tmdData.data(), tmdData.size()))
{
SET_FST_ERROR(BAD_TITLE_TMD);
return nullptr;
}
// load ticket
FileStream* ticketFile = FileStream::openFile2(folderPath / "title.tik");
if (!ticketFile)
{
SET_FST_ERROR(TITLE_TIK_MISSING);
return nullptr;
}
std::vector<uint8> ticketData;
ticketFile->extract(ticketData);
delete ticketFile;
NCrypto::ETicketParser ticketParser;
if (!ticketParser.parse(ticketData.data(), ticketData.size()))
{
SET_FST_ERROR(BAD_TITLE_TIK);
return nullptr;
}
NCrypto::AesKey titleKey;
ticketParser.GetTitleKey(titleKey);
// open data source
@ -411,13 +447,15 @@ FSTVolume* FSTVolume::OpenFromContentFolder(fs::path folderPath)
}
// load FST
// fstSize = size of first cluster?
FSTVolume* fstVolume = FSTVolume::OpenFST(std::move(dataSource), 0, fstSize, &titleKey, fstHashMode);
FSTVolume* fstVolume = FSTVolume::OpenFST(std::move(dataSource), 0, fstSize, &titleKey, fstHashMode, &tmdParser);
if (fstVolume)
SET_FST_ERROR(OK);
return fstVolume;
}
FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode)
FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD)
{
cemu_assert_debug(fstHashMode != ClusterHashMode::RAW || fstHashMode != ClusterHashMode::RAW2);
cemu_assert_debug(fstHashMode != ClusterHashMode::RAW || fstHashMode != ClusterHashMode::RAW_STREAM);
if (fstSize < sizeof(FSTHeader))
return nullptr;
constexpr uint64 FST_CLUSTER_OFFSET = 0;
@ -448,6 +486,34 @@ FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint3
clusterTable[i].offset = clusterDataTable[i].offset;
clusterTable[i].size = clusterDataTable[i].size;
clusterTable[i].hashMode = static_cast<FSTVolume::ClusterHashMode>((uint8)clusterDataTable[i].hashMode);
clusterTable[i].hasContentHash = false; // from the TMD file (H4?)
}
// if the TMD is available (when opening .app files) we can use the extra info from it to validate unhashed clusters
// each content entry in the TMD corresponds to one cluster used by the FST
if(optionalTMD)
{
if(numCluster != optionalTMD->GetContentList().size())
{
cemuLog_log(LogType::Force, "FST: Number of clusters does not match TMD content list");
return nullptr;
}
auto& contentList = optionalTMD->GetContentList();
for(size_t i=0; i<contentList.size(); i++)
{
auto& cluster = clusterTable[i];
auto& content = contentList[i];
cluster.hasContentHash = true;
cluster.contentHashIsSHA1 = HAS_FLAG(contentList[i].contentFlags, NCrypto::TMDParser::TMDContentFlags::FLAG_SHA1);
cluster.contentSize = content.size;
static_assert(sizeof(content.hash32) == sizeof(cluster.contentHash32));
memcpy(cluster.contentHash32, content.hash32, sizeof(cluster.contentHash32));
// if unhashed mode, then initialize the hash context
if(cluster.hashMode == ClusterHashMode::RAW || cluster.hashMode == ClusterHashMode::RAW_STREAM)
{
cluster.singleHashCtx.reset(EVP_MD_CTX_new());
EVP_DigestInit_ex(cluster.singleHashCtx.get(), cluster.contentHashIsSHA1 ? EVP_sha1() : EVP_sha256(), nullptr);
}
}
}
// preprocess FST table
FSTHeader_FileEntry* fileTable = (FSTHeader_FileEntry*)(clusterDataTable + numCluster);
@ -474,16 +540,17 @@ FSTVolume* FSTVolume::OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint3
fstVolume->m_offsetFactor = fstHeader->offsetFactor;
fstVolume->m_sectorSize = DISC_SECTOR_SIZE;
fstVolume->m_partitionTitlekey = *partitionTitleKey;
std::swap(fstVolume->m_cluster, clusterTable);
std::swap(fstVolume->m_entries, fstEntries);
std::swap(fstVolume->m_nameStringTable, nameStringTable);
fstVolume->m_hashIsDisabled = fstHeader->hashIsDisabled != 0;
fstVolume->m_cluster = std::move(clusterTable);
fstVolume->m_entries = std::move(fstEntries);
fstVolume->m_nameStringTable = std::move(nameStringTable);
return fstVolume;
}
FSTVolume* FSTVolume::OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode)
FSTVolume* FSTVolume::OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD)
{
FSTDataSource* ds = dataSource.release();
FSTVolume* fstVolume = OpenFST(ds, fstOffset, fstSize, partitionTitleKey, fstHashMode);
FSTVolume* fstVolume = OpenFST(ds, fstOffset, fstSize, partitionTitleKey, fstHashMode, optionalTMD);
if (!fstVolume)
{
delete ds;
@ -669,25 +736,25 @@ bool FSTVolume::OpenFile(std::string_view path, FSTFileHandle& fileHandleOut, bo
return true;
}
bool FSTVolume::IsDirectory(FSTFileHandle& fileHandle) const
bool FSTVolume::IsDirectory(const FSTFileHandle& fileHandle) const
{
cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size());
return m_entries[fileHandle.m_fstIndex].GetType() == FSTEntry::TYPE::DIRECTORY;
};
bool FSTVolume::IsFile(FSTFileHandle& fileHandle) const
bool FSTVolume::IsFile(const FSTFileHandle& fileHandle) const
{
cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size());
return m_entries[fileHandle.m_fstIndex].GetType() == FSTEntry::TYPE::FILE;
};
bool FSTVolume::HasLinkFlag(FSTFileHandle& fileHandle) const
bool FSTVolume::HasLinkFlag(const FSTFileHandle& fileHandle) const
{
cemu_assert_debug(fileHandle.m_fstIndex < m_entries.size());
return HAS_FLAG(m_entries[fileHandle.m_fstIndex].GetFlags(), FSTEntry::FLAGS::FLAG_LINK);
};
std::string_view FSTVolume::GetName(FSTFileHandle& fileHandle) const
std::string_view FSTVolume::GetName(const FSTFileHandle& fileHandle) const
{
if (fileHandle.m_fstIndex > m_entries.size())
return "";
@ -695,7 +762,7 @@ std::string_view FSTVolume::GetName(FSTFileHandle& fileHandle) const
return entryName;
}
std::string FSTVolume::GetPath(FSTFileHandle& fileHandle) const
std::string FSTVolume::GetPath(const FSTFileHandle& fileHandle) const
{
std::string path;
auto& entry = m_entries[fileHandle.m_fstIndex];
@ -726,7 +793,7 @@ std::string FSTVolume::GetPath(FSTFileHandle& fileHandle) const
return path;
}
uint32 FSTVolume::GetFileSize(FSTFileHandle& fileHandle) const
uint32 FSTVolume::GetFileSize(const FSTFileHandle& fileHandle) const
{
if (m_entries[fileHandle.m_fstIndex].GetType() != FSTEntry::TYPE::FILE)
return 0;
@ -740,7 +807,7 @@ uint32 FSTVolume::ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size
return 0;
cemu_assert_debug(!HAS_FLAG(entry.GetFlags(), FSTEntry::FLAGS::FLAG_LINK));
FSTCluster& cluster = m_cluster[entry.fileInfo.clusterIndex];
if (cluster.hashMode == ClusterHashMode::RAW || cluster.hashMode == ClusterHashMode::RAW2)
if (cluster.hashMode == ClusterHashMode::RAW || cluster.hashMode == ClusterHashMode::RAW_STREAM)
return ReadFile_HashModeRaw(entry.fileInfo.clusterIndex, entry, offset, size, dataOut);
else if (cluster.hashMode == ClusterHashMode::HASH_INTERLEAVED)
return ReadFile_HashModeHashed(entry.fileInfo.clusterIndex, entry, offset, size, dataOut);
@ -748,87 +815,15 @@ uint32 FSTVolume::ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size
return 0;
}
uint32 FSTVolume::ReadFile_HashModeRaw(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut)
{
const uint32 readSizeInput = readSize;
uint8* dataOutU8 = (uint8*)dataOut;
if (readOffset >= entry.fileInfo.fileSize)
return 0;
else if ((readOffset + readSize) >= entry.fileInfo.fileSize)
readSize = (entry.fileInfo.fileSize - readOffset);
const FSTCluster& cluster = m_cluster[clusterIndex];
uint64 clusterOffset = (uint64)cluster.offset * m_sectorSize;
uint64 absFileOffset = entry.fileInfo.fileOffset * m_offsetFactor + readOffset;
// make sure the raw range we read is aligned to AES block size (16)
uint64 readAddrStart = absFileOffset & ~0xF;
uint64 readAddrEnd = (absFileOffset + readSize + 0xF) & ~0xF;
bool usesInitialIV = readOffset < 16;
if (!usesInitialIV)
readAddrStart -= 16; // read previous AES block since we require it for the IV
uint32 prePadding = (uint32)(absFileOffset - readAddrStart); // number of extra bytes we read before readOffset (for AES alignment and IV calculation)
uint32 postPadding = (uint32)(readAddrEnd - (absFileOffset + readSize));
uint8 readBuffer[64 * 1024];
// read first chunk
// if file read offset (readOffset) is within the first AES-block then use initial IV calculated from cluster index
// otherwise read previous AES-block is the IV (AES-CBC)
uint64 readAddrCurrent = readAddrStart;
uint32 rawBytesToRead = (uint32)std::min((readAddrEnd - readAddrStart), (uint64)sizeof(readBuffer));
if (m_dataSource->readData(clusterIndex, clusterOffset, readAddrCurrent, readBuffer, rawBytesToRead) != rawBytesToRead)
{
cemuLog_log(LogType::Force, "FST read error in raw content");
return 0;
}
readAddrCurrent += rawBytesToRead;
uint8 iv[16]{};
if (usesInitialIV)
{
// for the first AES block, the IV is initialized from cluster index
iv[0] = (uint8)(clusterIndex >> 8);
iv[1] = (uint8)(clusterIndex >> 0);
AES128_CBC_decrypt_updateIV(readBuffer, readBuffer, rawBytesToRead, m_partitionTitlekey.b, iv);
std::memcpy(dataOutU8, readBuffer + prePadding, rawBytesToRead - prePadding - postPadding);
dataOutU8 += (rawBytesToRead - prePadding - postPadding);
readSize -= (rawBytesToRead - prePadding - postPadding);
}
else
{
// IV is initialized from previous AES block (AES-CBC)
std::memcpy(iv, readBuffer, 16);
AES128_CBC_decrypt_updateIV(readBuffer + 16, readBuffer + 16, rawBytesToRead - 16, m_partitionTitlekey.b, iv);
std::memcpy(dataOutU8, readBuffer + prePadding, rawBytesToRead - prePadding - postPadding);
dataOutU8 += (rawBytesToRead - prePadding - postPadding);
readSize -= (rawBytesToRead - prePadding - postPadding);
}
// read remaining chunks
while (readSize > 0)
{
uint32 bytesToRead = (uint32)std::min((uint32)sizeof(readBuffer), readSize);
uint32 alignedBytesToRead = (bytesToRead + 15) & ~0xF;
if (m_dataSource->readData(clusterIndex, clusterOffset, readAddrCurrent, readBuffer, alignedBytesToRead) != alignedBytesToRead)
{
cemuLog_log(LogType::Force, "FST read error in raw content");
return 0;
}
AES128_CBC_decrypt_updateIV(readBuffer, readBuffer, alignedBytesToRead, m_partitionTitlekey.b, iv);
std::memcpy(dataOutU8, readBuffer, bytesToRead);
dataOutU8 += bytesToRead;
readSize -= bytesToRead;
readAddrCurrent += alignedBytesToRead;
}
return readSizeInput - readSize;
}
constexpr size_t BLOCK_SIZE = 0x10000;
constexpr size_t BLOCK_HASH_SIZE = 0x0400;
constexpr size_t BLOCK_FILE_SIZE = 0xFC00;
struct FSTRawBlock
{
std::vector<uint8> rawData; // unhashed block size depends on sector size field in partition header
};
struct FSTHashedBlock
{
uint8 rawData[BLOCK_SIZE];
@ -870,12 +865,160 @@ struct FSTHashedBlock
static_assert(sizeof(FSTHashedBlock) == BLOCK_SIZE);
struct FSTCachedRawBlock
{
FSTRawBlock blockData;
uint8 ivForNextBlock[16];
uint64 lastAccess;
};
struct FSTCachedHashedBlock
{
FSTHashedBlock blockData;
uint64 lastAccess;
};
// Checks cache fill state and if necessary drops least recently accessed block from the cache. Optionally allows to recycle the released cache entry to cut down cost of memory allocation and clearing
void FSTVolume::TrimCacheIfRequired(FSTCachedRawBlock** droppedRawBlock, FSTCachedHashedBlock** droppedHashedBlock)
{
// calculate size used by cache
size_t cacheSize = 0;
for (auto& itr : m_cacheDecryptedRawBlocks)
cacheSize += itr.second->blockData.rawData.size();
for (auto& itr : m_cacheDecryptedHashedBlocks)
cacheSize += sizeof(FSTCachedHashedBlock) + sizeof(FSTHashedBlock);
// only trim if cache is full (larger than 2MB)
if (cacheSize < 2*1024*1024) // 2MB
return;
// scan both cache lists to find least recently accessed block to drop
auto dropRawItr = std::min_element(m_cacheDecryptedRawBlocks.begin(), m_cacheDecryptedRawBlocks.end(), [](const auto& a, const auto& b) -> bool
{ return a.second->lastAccess < b.second->lastAccess; });
auto dropHashedItr = std::min_element(m_cacheDecryptedHashedBlocks.begin(), m_cacheDecryptedHashedBlocks.end(), [](const auto& a, const auto& b) -> bool
{ return a.second->lastAccess < b.second->lastAccess; });
uint64 lastAccess = std::numeric_limits<uint64>::max();
if(dropRawItr != m_cacheDecryptedRawBlocks.end())
lastAccess = dropRawItr->second->lastAccess;
if(dropHashedItr != m_cacheDecryptedHashedBlocks.end())
lastAccess = std::min<uint64>(lastAccess, dropHashedItr->second->lastAccess);
if(dropRawItr != m_cacheDecryptedRawBlocks.end() && dropRawItr->second->lastAccess == lastAccess)
{
if (droppedRawBlock)
*droppedRawBlock = dropRawItr->second;
else
delete dropRawItr->second;
m_cacheDecryptedRawBlocks.erase(dropRawItr);
return;
}
else if(dropHashedItr != m_cacheDecryptedHashedBlocks.end() && dropHashedItr->second->lastAccess == lastAccess)
{
if (droppedHashedBlock)
*droppedHashedBlock = dropHashedItr->second;
else
delete dropHashedItr->second;
m_cacheDecryptedHashedBlocks.erase(dropHashedItr);
}
}
void FSTVolume::DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, uint8 ivOut[16])
{
memset(ivOut, 0, sizeof(ivOut));
if(blockIndex == 0)
{
ivOut[0] = (uint8)(clusterIndex >> 8);
ivOut[1] = (uint8)(clusterIndex >> 0);
}
else
{
// the last 16 encrypted bytes of the previous block are the IV (AES CBC)
// if the previous block is cached we can grab the IV from there. Otherwise we have to read the 16 bytes from the data source
uint32 prevBlockIndex = blockIndex - 1;
uint64 cacheBlockId = ((uint64)clusterIndex << (64 - 16)) | (uint64)prevBlockIndex;
auto itr = m_cacheDecryptedRawBlocks.find(cacheBlockId);
if (itr != m_cacheDecryptedRawBlocks.end())
{
memcpy(ivOut, itr->second->ivForNextBlock, 16);
}
else
{
cemu_assert(m_sectorSize >= 16);
uint64 clusterOffset = (uint64)m_cluster[clusterIndex].offset * m_sectorSize;
uint8 prevIV[16];
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * m_sectorSize - 16, prevIV, 16) != 16)
{
cemuLog_log(LogType::Force, "Failed to read IV for raw FST block");
m_detectedCorruption = true;
return;
}
memcpy(ivOut, prevIV, 16);
}
}
}
FSTCachedRawBlock* FSTVolume::GetDecryptedRawBlock(uint32 clusterIndex, uint32 blockIndex)
{
FSTCluster& cluster = m_cluster[clusterIndex];
uint64 clusterOffset = (uint64)cluster.offset * m_sectorSize;
// generate id for cache
uint64 cacheBlockId = ((uint64)clusterIndex << (64 - 16)) | (uint64)blockIndex;
// lookup block in cache
FSTCachedRawBlock* block = nullptr;
auto itr = m_cacheDecryptedRawBlocks.find(cacheBlockId);
if (itr != m_cacheDecryptedRawBlocks.end())
{
block = itr->second;
block->lastAccess = ++m_cacheAccessCounter;
return block;
}
// if cache already full, drop least recently accessed block and recycle FSTCachedRawBlock object if possible
TrimCacheIfRequired(&block, nullptr);
if (!block)
block = new FSTCachedRawBlock();
block->blockData.rawData.resize(m_sectorSize);
// block not cached, read new
block->lastAccess = ++m_cacheAccessCounter;
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * m_sectorSize, block->blockData.rawData.data(), m_sectorSize) != m_sectorSize)
{
cemuLog_log(LogType::Force, "Failed to read raw FST block");
delete block;
m_detectedCorruption = true;
return nullptr;
}
// decrypt hash data
uint8 iv[16]{};
DetermineUnhashedBlockIV(clusterIndex, blockIndex, iv);
memcpy(block->ivForNextBlock, block->blockData.rawData.data() + m_sectorSize - 16, 16);
AES128_CBC_decrypt(block->blockData.rawData.data(), block->blockData.rawData.data(), m_sectorSize, m_partitionTitlekey.b, iv);
// if this is the next block, then hash it
if(cluster.hasContentHash)
{
if(cluster.singleHashNumBlocksHashed == blockIndex)
{
cemu_assert_debug(!(cluster.contentSize % m_sectorSize)); // size should be multiple of sector size? Regardless, the hashing code below can handle non-aligned sizes
bool isLastBlock = blockIndex == (std::max<uint32>(cluster.contentSize / m_sectorSize, 1) - 1);
uint32 hashSize = m_sectorSize;
if(isLastBlock)
hashSize = cluster.contentSize - (uint64)blockIndex*m_sectorSize;
EVP_DigestUpdate(cluster.singleHashCtx.get(), block->blockData.rawData.data(), hashSize);
cluster.singleHashNumBlocksHashed++;
if(isLastBlock)
{
uint8 hash[32];
EVP_DigestFinal_ex(cluster.singleHashCtx.get(), hash, nullptr);
if(memcmp(hash, cluster.contentHash32, cluster.contentHashIsSHA1 ? 20 : 32) != 0)
{
cemuLog_log(LogType::Force, "FST: Raw section hash mismatch");
delete block;
m_detectedCorruption = true;
return nullptr;
}
}
}
}
// register in cache
m_cacheDecryptedRawBlocks.emplace(cacheBlockId, block);
return block;
}
FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex)
{
const FSTCluster& cluster = m_cluster[clusterIndex];
@ -891,22 +1034,17 @@ FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, ui
block->lastAccess = ++m_cacheAccessCounter;
return block;
}
// if cache already full, drop least recently accessed block (but recycle the FSTHashedBlock* object)
if (m_cacheDecryptedHashedBlocks.size() >= 16)
{
auto dropItr = std::min_element(m_cacheDecryptedHashedBlocks.begin(), m_cacheDecryptedHashedBlocks.end(), [](const auto& a, const auto& b) -> bool
{ return a.second->lastAccess < b.second->lastAccess; });
block = dropItr->second;
m_cacheDecryptedHashedBlocks.erase(dropItr);
}
else
// if cache already full, drop least recently accessed block and recycle FSTCachedHashedBlock object if possible
TrimCacheIfRequired(nullptr, &block);
if (!block)
block = new FSTCachedHashedBlock();
// block not cached, read new
block->lastAccess = ++m_cacheAccessCounter;
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * BLOCK_SIZE, block->blockData.rawData, BLOCK_SIZE) != BLOCK_SIZE)
{
cemuLog_log(LogType::Force, "Failed to read FST block");
cemuLog_log(LogType::Force, "Failed to read hashed FST block");
delete block;
m_detectedCorruption = true;
return nullptr;
}
// decrypt hash data
@ -914,11 +1052,46 @@ FSTCachedHashedBlock* FSTVolume::GetDecryptedHashedBlock(uint32 clusterIndex, ui
AES128_CBC_decrypt(block->blockData.getHashData(), block->blockData.getHashData(), BLOCK_HASH_SIZE, m_partitionTitlekey.b, iv);
// decrypt file data
AES128_CBC_decrypt(block->blockData.getFileData(), block->blockData.getFileData(), BLOCK_FILE_SIZE, m_partitionTitlekey.b, block->blockData.getH0Hash(blockIndex%16));
// compare with H0 to verify data integrity
NCrypto::CHash160 h0;
SHA1(block->blockData.getFileData(), BLOCK_FILE_SIZE, h0.b);
uint32 h0Index = (blockIndex % 4096);
if (memcmp(h0.b, block->blockData.getH0Hash(h0Index & 0xF), sizeof(h0.b)) != 0)
{
cemuLog_log(LogType::Force, "FST: Hash H0 mismatch in hashed block (section {} index {})", clusterIndex, blockIndex);
delete block;
m_detectedCorruption = true;
return nullptr;
}
// register in cache
m_cacheDecryptedHashedBlocks.emplace(cacheBlockId, block);
return block;
}
uint32 FSTVolume::ReadFile_HashModeRaw(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut)
{
uint8* dataOutU8 = (uint8*)dataOut;
if (readOffset >= entry.fileInfo.fileSize)
return 0;
else if ((readOffset + readSize) >= entry.fileInfo.fileSize)
readSize = (entry.fileInfo.fileSize - readOffset);
uint64 absFileOffset = entry.fileInfo.fileOffset * m_offsetFactor + readOffset;
uint32 remainingReadSize = readSize;
while (remainingReadSize > 0)
{
const FSTCachedRawBlock* rawBlock = this->GetDecryptedRawBlock(clusterIndex, absFileOffset/m_sectorSize);
if (!rawBlock)
break;
uint32 blockOffset = (uint32)(absFileOffset % m_sectorSize);
uint32 bytesToRead = std::min<uint32>(remainingReadSize, m_sectorSize - blockOffset);
std::memcpy(dataOutU8, rawBlock->blockData.rawData.data() + blockOffset, bytesToRead);
dataOutU8 += bytesToRead;
remainingReadSize -= bytesToRead;
absFileOffset += bytesToRead;
}
return readSize - remainingReadSize;
}
uint32 FSTVolume::ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut)
{
/*
@ -949,7 +1122,6 @@ uint32 FSTVolume::ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry,
*/
const FSTCluster& cluster = m_cluster[clusterIndex];
uint64 clusterBaseOffset = (uint64)cluster.offset * m_sectorSize;
uint64 fileReadOffset = entry.fileInfo.fileOffset * m_offsetFactor + readOffset;
uint32 blockIndex = (uint32)(fileReadOffset / BLOCK_FILE_SIZE);
uint32 bytesRemaining = readSize;
@ -977,6 +1149,7 @@ bool FSTVolume::OpenDirectoryIterator(std::string_view path, FSTDirectoryIterato
if (!IsDirectory(fileHandle))
return false;
auto const& fstEntry = m_entries[fileHandle.m_fstIndex];
directoryIteratorOut.dirHandle = fileHandle;
directoryIteratorOut.startIndex = fileHandle.m_fstIndex + 1;
directoryIteratorOut.endIndex = fstEntry.dirInfo.endIndex;
directoryIteratorOut.currentIndex = directoryIteratorOut.startIndex;
@ -1001,6 +1174,8 @@ bool FSTVolume::Next(FSTDirectoryIterator& directoryIterator, FSTFileHandle& fil
FSTVolume::~FSTVolume()
{
for (auto& itr : m_cacheDecryptedRawBlocks)
delete itr.second;
for (auto& itr : m_cacheDecryptedHashedBlocks)
delete itr.second;
if (m_sourceIsOwned)
@ -1097,4 +1272,4 @@ bool FSTVerifier::VerifyHashedContentFile(FileStream* fileContent, const NCrypto
void FSTVolumeTest()
{
FSTPathUnitTest();
}
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "Cemu/ncrypto/ncrypto.h"
#include "openssl/evp.h"
struct FSTFileHandle
{
@ -11,7 +12,13 @@ private:
struct FSTDirectoryIterator
{
friend class FSTVolume;
const FSTFileHandle& GetDirHandle() const
{
return dirHandle;
}
private:
FSTFileHandle dirHandle;
uint32 startIndex;
uint32 endIndex;
uint32 currentIndex;
@ -20,28 +27,39 @@ private:
class FSTVolume
{
public:
enum class ErrorCode
{
OK = 0,
UNKNOWN_ERROR = 1,
DISC_KEY_MISSING = 2,
TITLE_TIK_MISSING = 3,
BAD_TITLE_TMD = 4,
BAD_TITLE_TIK = 5,
};
static bool FindDiscKey(const fs::path& path, NCrypto::AesKey& discTitleKey);
static FSTVolume* OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& discTitleKey);
static FSTVolume* OpenFromDiscImage(const fs::path& path, bool* keyFound = nullptr);
static FSTVolume* OpenFromContentFolder(fs::path folderPath);
static FSTVolume* OpenFromDiscImage(const fs::path& path, NCrypto::AesKey& discTitleKey, ErrorCode* errorCodeOut = nullptr);
static FSTVolume* OpenFromDiscImage(const fs::path& path, ErrorCode* errorCodeOut = nullptr);
static FSTVolume* OpenFromContentFolder(fs::path folderPath, ErrorCode* errorCodeOut = nullptr);
~FSTVolume();
uint32 GetFileCount() const;
bool HasCorruption() const { return m_detectedCorruption; }
bool OpenFile(std::string_view path, FSTFileHandle& fileHandleOut, bool openOnlyFiles = false);
// file and directory functions
bool IsDirectory(FSTFileHandle& fileHandle) const;
bool IsFile(FSTFileHandle& fileHandle) const;
bool HasLinkFlag(FSTFileHandle& fileHandle) const;
bool IsDirectory(const FSTFileHandle& fileHandle) const;
bool IsFile(const FSTFileHandle& fileHandle) const;
bool HasLinkFlag(const FSTFileHandle& fileHandle) const;
std::string_view GetName(FSTFileHandle& fileHandle) const;
std::string GetPath(FSTFileHandle& fileHandle) const;
std::string_view GetName(const FSTFileHandle& fileHandle) const;
std::string GetPath(const FSTFileHandle& fileHandle) const;
// file functions
uint32 GetFileSize(FSTFileHandle& fileHandle) const;
uint32 GetFileSize(const FSTFileHandle& fileHandle) const;
uint32 ReadFile(FSTFileHandle& fileHandle, uint32 offset, uint32 size, void* dataOut);
// directory iterator
@ -70,15 +88,25 @@ private:
enum class ClusterHashMode : uint8
{
RAW = 0, // raw data + encryption, no hashing?
RAW2 = 1, // raw data + encryption, with hash stored in tmd?
RAW_STREAM = 1, // raw data + encryption, with hash stored in tmd?
HASH_INTERLEAVED = 2, // hashes + raw interleaved in 0x10000 blocks (0x400 bytes of hashes at the beginning, followed by 0xFC00 bytes of data)
};
struct FSTCluster
{
FSTCluster() : singleHashCtx(nullptr, &EVP_MD_CTX_free) {}
uint32 offset;
uint32 size;
ClusterHashMode hashMode;
// extra data if TMD is available
bool hasContentHash;
uint8 contentHash32[32];
bool contentHashIsSHA1; // if true then it's SHA1 (with extra bytes zeroed out), otherwise it's SHA256
uint64 contentSize; // size of the content (in blocks)
// hash context for single hash mode (content hash must be available)
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> singleHashCtx; // unique_ptr to make this move-only
uint32 singleHashNumBlocksHashed{0};
};
struct FSTEntry
@ -148,17 +176,30 @@ private:
bool m_sourceIsOwned{};
uint32 m_sectorSize{}; // for cluster offsets
uint32 m_offsetFactor{}; // for file offsets
bool m_hashIsDisabled{}; // disables hash verification (for all clusters of this volume?)
std::vector<FSTCluster> m_cluster;
std::vector<FSTEntry> m_entries;
std::vector<char> m_nameStringTable;
NCrypto::AesKey m_partitionTitlekey;
bool m_detectedCorruption{false};
/* Cache for decrypted hashed blocks */
bool HashIsDisabled() const
{
return m_hashIsDisabled;
}
/* Cache for decrypted raw and hashed blocks */
std::unordered_map<uint64, struct FSTCachedRawBlock*> m_cacheDecryptedRawBlocks;
std::unordered_map<uint64, struct FSTCachedHashedBlock*> m_cacheDecryptedHashedBlocks;
uint64 m_cacheAccessCounter{};
void DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, uint8 ivOut[16]);
struct FSTCachedRawBlock* GetDecryptedRawBlock(uint32 clusterIndex, uint32 blockIndex);
struct FSTCachedHashedBlock* GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex);
void TrimCacheIfRequired(struct FSTCachedRawBlock** droppedRawBlock, struct FSTCachedHashedBlock** droppedHashedBlock);
/* File reading */
uint32 ReadFile_HashModeRaw(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut);
uint32 ReadFile_HashModeHashed(uint32 clusterIndex, FSTEntry& entry, uint32 readOffset, uint32 readSize, void* dataOut);
@ -169,7 +210,10 @@ private:
/* +0x00 */ uint32be magic;
/* +0x04 */ uint32be offsetFactor;
/* +0x08 */ uint32be numCluster;
/* +0x0C */ uint32be ukn0C;
/* +0x0C */ uint8be hashIsDisabled;
/* +0x0D */ uint8be ukn0D;
/* +0x0E */ uint8be ukn0E;
/* +0x0F */ uint8be ukn0F;
/* +0x10 */ uint32be ukn10;
/* +0x14 */ uint32be ukn14;
/* +0x18 */ uint32be ukn18;
@ -246,8 +290,8 @@ private:
static_assert(sizeof(FSTHeader_FileEntry) == 0x10);
static FSTVolume* OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode);
static FSTVolume* OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode);
static FSTVolume* OpenFST(FSTDataSource* dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD);
static FSTVolume* OpenFST(std::unique_ptr<FSTDataSource> dataSource, uint64 fstOffset, uint32 fstSize, NCrypto::AesKey* partitionTitleKey, ClusterHashMode fstHashMode, NCrypto::TMDParser* optionalTMD);
static bool ProcessFST(FSTHeader_FileEntry* fileTable, uint32 numFileEntries, uint32 numCluster, std::vector<char>& nameStringTable, std::vector<FSTEntry>& fstEntries);
bool MatchFSTEntryName(FSTEntry& entry, std::string_view comparedName)

View file

@ -1,5 +1,6 @@
#include <wx/msgdlg.h>
#include <mutex>
#include <gui/helpers/wxHelpers.h>
#include "config/ActiveSettings.h"
#include "util/crypto/aes128.h"
@ -74,7 +75,7 @@ void KeyCache_Prepare()
}
else
{
wxMessageBox("Unable to create file keys.txt\nThis can happen if Cemu does not have write permission to it's own directory, the disk is full or if anti-virus software is blocking Cemu.", "Error", wxOK | wxCENTRE | wxICON_ERROR);
wxMessageBox(_("Unable to create file keys.txt\nThis can happen if Cemu does not have write permission to its own directory, the disk is full or if anti-virus software is blocking Cemu."), _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
}
mtxKeyCache.unlock();
return;
@ -107,10 +108,8 @@ void KeyCache_Prepare()
continue;
if( strishex(line) == false )
{
// show error message
char errorMsg[512];
sprintf(errorMsg, "Error in keys.txt in line %d\n", lineNumber);
wxMessageBox(errorMsg, "Error", wxOK | wxCENTRE | wxICON_ERROR);
auto errorMsg = formatWxString(_("Error in keys.txt at line {}"), lineNumber);
wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
continue;
}
if(line.size() == 32 )

View file

@ -3,6 +3,8 @@
#include <boost/container/small_vector.hpp>
#include "../fsc.h"
// path parser and utility class for Wii U paths
// optimized to be allocation-free for common path lengths
class FSCPath
@ -119,9 +121,7 @@ public:
template<typename F>
class FSAFileTree
{
public:
private:
private:
enum NODETYPE : uint8
{
@ -133,6 +133,7 @@ private:
{
std::string name;
std::vector<node_t*> subnodes;
size_t fileSize;
F* custom;
NODETYPE type;
};
@ -179,13 +180,54 @@ private:
return newNode;
}
class DirectoryIterator : public FSCVirtualFile
{
public:
DirectoryIterator(node_t* node)
: m_node(node), m_subnodeIndex(0)
{
}
sint32 fscGetType() override
{
return FSC_TYPE_DIRECTORY;
}
bool fscDirNext(FSCDirEntry* dirEntry) override
{
if (m_subnodeIndex >= m_node->subnodes.size())
return false;
const node_t* subnode = m_node->subnodes[m_subnodeIndex];
strncpy(dirEntry->path, subnode->name.c_str(), sizeof(dirEntry->path) - 1);
dirEntry->path[sizeof(dirEntry->path) - 1] = '\0';
dirEntry->isDirectory = subnode->type == FSAFileTree::NODETYPE_DIRECTORY;
dirEntry->isFile = subnode->type == FSAFileTree::NODETYPE_FILE;
dirEntry->fileSize = subnode->type == FSAFileTree::NODETYPE_FILE ? subnode->fileSize : 0;
++m_subnodeIndex;
return true;
}
bool fscRewindDir() override
{
m_subnodeIndex = 0;
return true;
}
private:
node_t* m_node;
size_t m_subnodeIndex;
};
public:
FSAFileTree()
{
rootNode.type = NODETYPE_DIRECTORY;
}
bool addFile(std::string_view path, F* custom)
bool addFile(std::string_view path, size_t fileSize, F* custom)
{
FSCPath p(path);
if (p.GetNodeCount() == 0)
@ -196,6 +238,7 @@ public:
return false; // node already exists
// add file node
node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.GetNodeName(p.GetNodeCount() - 1));
fileNode->fileSize = fileSize;
fileNode->custom = custom;
return true;
}
@ -214,6 +257,20 @@ public:
return true;
}
bool getDirectory(std::string_view path, FSCVirtualFile*& dirIterator)
{
FSCPath p(path);
if (p.GetNodeCount() == 0)
return false;
node_t* node = getByNodePath(p, p.GetNodeCount(), false);
if (node == nullptr)
return false;
if (node->type != NODETYPE_DIRECTORY)
return false;
dirIterator = new DirectoryIterator(node);
return true;
}
bool removeFile(std::string_view path)
{
FSCPath p(path);

View file

@ -0,0 +1,40 @@
#pragma once
struct romfs_header_t
{
uint32 header_magic;
uint32be header_size;
uint64be dir_hash_table_ofs;
uint64be dir_hash_table_size;
uint64be dir_table_ofs;
uint64be dir_table_size;
uint64be file_hash_table_ofs;
uint64be file_hash_table_size;
uint64be file_table_ofs;
uint64be file_table_size;
uint64be file_partition_ofs;
};
struct romfs_direntry_t
{
uint32be parent;
uint32be listNext; // offset to next directory entry in linked list of parent directory (aka "sibling")
uint32be dirListHead; // offset to first entry in linked list of directory entries (aka "child")
uint32be fileListHead; // offset to first entry in linked list of file entries (aka "file")
uint32be hash;
uint32be name_size;
std::string name;
};
struct romfs_fentry_t
{
uint32be parent;
uint32be listNext; // offset to next file entry in linked list of parent directory (aka "sibling")
uint64be offset;
uint64be size;
uint32be hash;
uint32be name_size;
std::string name;
};
#define ROMFS_ENTRY_EMPTY 0xFFFFFFFF

View file

@ -0,0 +1,224 @@
#include "WUHBReader.h"
WUHBReader* WUHBReader::FromPath(const fs::path& path)
{
FileStream* fileIn{FileStream::openFile2(path)};
if (!fileIn)
return nullptr;
WUHBReader* ret = new WUHBReader(fileIn);
if (!ret->CheckMagicValue())
{
delete ret;
return nullptr;
}
if (!ret->ReadHeader())
{
delete ret;
return nullptr;
}
return ret;
}
static const romfs_direntry_t fallbackDirEntry{
.parent = ROMFS_ENTRY_EMPTY,
.listNext = ROMFS_ENTRY_EMPTY,
.dirListHead = ROMFS_ENTRY_EMPTY,
.fileListHead = ROMFS_ENTRY_EMPTY,
.hash = ROMFS_ENTRY_EMPTY,
.name_size = 0,
.name = ""
};
static const romfs_fentry_t fallbackFileEntry{
.parent = ROMFS_ENTRY_EMPTY,
.listNext = ROMFS_ENTRY_EMPTY,
.offset = 0,
.size = 0,
.hash = ROMFS_ENTRY_EMPTY,
.name_size = 0,
.name = ""
};
template<bool File>
const WUHBReader::EntryType<File>& WUHBReader::GetFallback()
{
if constexpr (File)
return fallbackFileEntry;
else
return fallbackDirEntry;
}
template<bool File>
WUHBReader::EntryType<File> WUHBReader::GetEntry(uint32 offset) const
{
auto fallback = GetFallback<File>();
if(offset == ROMFS_ENTRY_EMPTY)
return fallback;
const char* typeName = File ? "fentry" : "direntry";
EntryType<File> ret;
if (offset >= (File ? m_header.file_table_size : m_header.dir_table_size))
{
cemuLog_log(LogType::Force, "WUHB {} offset exceeds table size declared in header", typeName);
return fallback;
}
// read the entry
m_fileIn->SetPosition((File ? m_header.file_table_ofs : m_header.dir_table_ofs) + offset);
auto read = m_fileIn->readData(&ret, offsetof(EntryType<File>, name));
if (read != offsetof(EntryType<File>, name))
{
cemuLog_log(LogType::Force, "failed to read WUHB {} at offset: {}", typeName, offset);
return fallback;
}
// read the name
ret.name.resize(ret.name_size);
read = m_fileIn->readData(ret.name.data(), ret.name_size);
if (read != ret.name_size)
{
cemuLog_log(LogType::Force, "failed to read WUHB {} name", typeName);
return fallback;
}
return ret;
}
romfs_direntry_t WUHBReader::GetDirEntry(uint32 offset) const
{
return GetEntry<false>(offset);
}
romfs_fentry_t WUHBReader::GetFileEntry(uint32 offset) const
{
return GetEntry<true>(offset);
}
uint64 WUHBReader::GetFileSize(uint32 entryOffset) const
{
return GetFileEntry(entryOffset).size;
}
uint64 WUHBReader::ReadFromFile(uint32 entryOffset, uint64 fileOffset, uint64 length, void* buffer) const
{
const auto fileEntry = GetFileEntry(entryOffset);
if (fileOffset >= fileEntry.size)
return 0;
const uint64 readAmount = std::min(length, fileEntry.size - fileOffset);
const uint64 wuhbOffset = m_header.file_partition_ofs + fileEntry.offset + fileOffset;
m_fileIn->SetPosition(wuhbOffset);
return m_fileIn->readData(buffer, readAmount);
}
uint32 WUHBReader::GetHashTableEntryOffset(uint32 hash, bool isFile) const
{
const uint64 hash_table_size = (isFile ? m_header.file_hash_table_size : m_header.dir_hash_table_size);
const uint64 hash_table_ofs = (isFile ? m_header.file_hash_table_ofs : m_header.dir_hash_table_ofs);
const uint64 hash_table_entry_count = hash_table_size / sizeof(uint32);
const uint64 hash_table_entry_offset = hash_table_ofs + (hash % hash_table_entry_count) * sizeof(uint32);
m_fileIn->SetPosition(hash_table_entry_offset);
uint32 tableOffset;
if (!m_fileIn->readU32(tableOffset))
{
cemuLog_log(LogType::Force, "failed to read WUHB hash table entry at file offset: {}", hash_table_entry_offset);
return ROMFS_ENTRY_EMPTY;
}
return uint32be::from_bevalue(tableOffset);
}
template<bool T>
bool WUHBReader::SearchHashList(uint32& entryOffset, const fs::path& targetName) const
{
for (;;)
{
if (entryOffset == ROMFS_ENTRY_EMPTY)
return false;
auto entry = GetEntry<T>(entryOffset);
if (entry.name == targetName)
return true;
entryOffset = entry.hash;
}
return false;
}
uint32 WUHBReader::Lookup(const std::filesystem::path& path, bool isFile) const
{
uint32 currentEntryOffset = 0;
auto look = [&](const fs::path& part, bool lookInFileHT) {
const auto partString = part.string();
currentEntryOffset = GetHashTableEntryOffset(CalcPathHash(currentEntryOffset, partString.c_str(), 0, partString.size()), lookInFileHT);
if (lookInFileHT)
return SearchHashList<true>(currentEntryOffset, part);
else
return SearchHashList<false>(currentEntryOffset, part);
};
// look for the root entry
if (!look("", false))
return ROMFS_ENTRY_EMPTY;
auto it = path.begin();
while (it != path.end())
{
fs::path part = *it;
++it;
// no need to recurse after trailing forward slash (e.g. directory/)
if (part.empty() && !isFile)
break;
// skip leading forward slash
if (part == "/")
continue;
// if the lookup target is a file and this is the last iteration, look in the file hash table instead.
if (!look(part, it == path.end() && isFile))
return ROMFS_ENTRY_EMPTY;
}
return currentEntryOffset;
}
bool WUHBReader::CheckMagicValue() const
{
uint8 magic[4];
m_fileIn->SetPosition(0);
int read = m_fileIn->readData(magic, 4);
if (read != 4)
{
cemuLog_log(LogType::Force, "Failed to read WUHB magic numbers");
return false;
}
static_assert(sizeof(magic) == s_headerMagicValue.size());
return std::memcmp(&magic, s_headerMagicValue.data(), sizeof(magic)) == 0;
}
bool WUHBReader::ReadHeader()
{
m_fileIn->SetPosition(0);
auto read = m_fileIn->readData(&m_header, sizeof(m_header));
auto readSuccess = read == sizeof(m_header);
if (!readSuccess)
cemuLog_log(LogType::Force, "Failed to read WUHB header");
return readSuccess;
}
unsigned char WUHBReader::NormalizeChar(unsigned char c)
{
if (c >= 'a' && c <= 'z')
{
return c + 'A' - 'a';
}
else
{
return c;
}
}
uint32 WUHBReader::CalcPathHash(uint32 parent, const char* path, uint32 start, size_t path_len)
{
cemu_assert(path != nullptr || path_len == 0);
uint32 hash = parent ^ 123456789;
for (uint32 i = 0; i < path_len; i++)
{
hash = (hash >> 5) | (hash << 27);
hash ^= NormalizeChar(path[start + i]);
}
return hash;
}

View file

@ -0,0 +1,45 @@
#pragma once
#include <Common/FileStream.h>
#include "RomFSStructs.h"
class WUHBReader
{
public:
static WUHBReader* FromPath(const fs::path& path);
romfs_direntry_t GetDirEntry(uint32 offset) const;
romfs_fentry_t GetFileEntry(uint32 offset) const;
uint64 GetFileSize(uint32 entryOffset) const;
uint64 ReadFromFile(uint32 entryOffset, uint64 fileOffset, uint64 length, void* buffer) const;
uint32 Lookup(const std::filesystem::path& path, bool isFile) const;
private:
WUHBReader(FileStream* file)
: m_fileIn(file)
{
cemu_assert_debug(file != nullptr);
};
WUHBReader() = delete;
romfs_header_t m_header;
std::unique_ptr<FileStream> m_fileIn;
constexpr static std::string_view s_headerMagicValue = "WUHB";
bool ReadHeader();
bool CheckMagicValue() const;
static inline unsigned char NormalizeChar(unsigned char c);
static uint32 CalcPathHash(uint32 parent, const char* path, uint32 start, size_t path_len);
template<bool File>
using EntryType = std::conditional_t<File, romfs_fentry_t, romfs_direntry_t>;
template<bool File>
static const EntryType<File>& GetFallback();
template<bool File>
EntryType<File> GetEntry(uint32 offset) const;
template<bool T>
bool SearchHashList(uint32& entryOffset, const fs::path& targetName) const;
uint32 GetHashTableEntryOffset(uint32 hash, bool isFile) const;
};

View file

@ -6,7 +6,7 @@ struct FSCMountPathNode
std::string path;
std::vector<FSCMountPathNode*> subnodes;
FSCMountPathNode* parent;
// device target and path (if subnodes is empty)
// associated device target and path
fscDeviceC* device{ nullptr };
void* ctx{ nullptr };
std::string deviceTargetPath; // the destination base path for the device, utf8
@ -17,6 +17,25 @@ struct FSCMountPathNode
{
}
void AssignDevice(fscDeviceC* device, void* ctx, std::string_view deviceBasePath)
{
this->device = device;
this->ctx = ctx;
this->deviceTargetPath = deviceBasePath;
}
void UnassignDevice()
{
this->device = nullptr;
this->ctx = nullptr;
this->deviceTargetPath.clear();
}
bool IsRootNode() const
{
return !parent;
}
~FSCMountPathNode()
{
for (auto& itr : subnodes)
@ -141,9 +160,7 @@ sint32 fsc_mount(std::string_view mountPath, std::string_view targetPath, fscDev
fscLeave();
return FSC_STATUS_INVALID_PATH;
}
node->device = fscDevice;
node->ctx = ctx;
node->deviceTargetPath = std::move(targetPathWithSlash);
node->AssignDevice(fscDevice, ctx, targetPathWithSlash);
fscLeave();
return FSC_STATUS_OK;
}
@ -160,14 +177,13 @@ bool fsc_unmount(std::string_view mountPath, sint32 priority)
}
cemu_assert(mountPathNode->priority == priority);
cemu_assert(mountPathNode->device);
// delete node
while (mountPathNode && mountPathNode->parent)
// unassign device
mountPathNode->UnassignDevice();
// prune empty branch
while (mountPathNode && !mountPathNode->IsRootNode() && mountPathNode->subnodes.empty() && !mountPathNode->device)
{
FSCMountPathNode* parent = mountPathNode->parent;
cemu_assert(!(!mountPathNode->subnodes.empty() && mountPathNode->device));
if (!mountPathNode->subnodes.empty())
break;
parent->subnodes.erase(std::find(parent->subnodes.begin(), parent->subnodes.end(), mountPathNode));
std::erase(parent->subnodes, mountPathNode);
delete mountPathNode;
mountPathNode = parent;
}
@ -302,6 +318,15 @@ public:
return true;
}
bool fscRewindDir() override
{
if (!dirIterator)
return true;
dirIterator->index = 0;
return true;
}
void addUniqueDirEntry(const FSCDirEntry& dirEntry)
{
// skip if already in list
@ -378,6 +403,7 @@ FSCVirtualFile* fsc_open(const char* path, FSC_ACCESS_FLAG accessFlags, sint32*
{
// return first found file
cemu_assert_debug(HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE));
fscVirtualFile->m_isAppend = HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::IS_APPEND);
fscLeave();
return fscVirtualFile;
}
@ -541,7 +567,7 @@ void fsc_setFileLength(FSCVirtualFile* fscFile, uint32 newEndOffset)
uint32 fileSize = fsc_getFileSize(fscFile);
if (!fsc_isWritable(fscFile))
{
cemuLog_force("TruncateFile called on read-only file");
cemuLog_log(LogType::Force, "TruncateFile called on read-only file");
}
else
{
@ -598,6 +624,9 @@ uint32 fsc_writeFile(FSCVirtualFile* fscFile, void* buffer, uint32 size)
fscLeave();
return 0;
}
if (fscFile->m_isAppend)
fsc_setFileSeek(fscFile, fsc_getFileSize(fscFile));
uint32 fscStatus = fscFile->fscWriteData(buffer, size);
fscLeave();
return fscStatus;

View file

@ -24,7 +24,10 @@ enum class FSC_ACCESS_FLAG : uint8
// which types can be opened
// invalid operation if neither is set
OPEN_DIR = (1 << 4),
OPEN_FILE = (1 << 5)
OPEN_FILE = (1 << 5),
// Writing seeks to the end of the file if set
IS_APPEND = (1 << 6)
};
DEFINE_ENUM_FLAG_OPERATORS(FSC_ACCESS_FLAG);
@ -149,7 +152,15 @@ struct FSCVirtualFile
return false;
}
virtual bool fscRewindDir()
{
cemu_assert_unimplemented();
return false;
}
FSCDirIteratorState* dirIterator{};
bool m_isAppend{ false };
};
#define FSC_PRIORITY_BASE (0)
@ -193,10 +204,12 @@ bool FSCDeviceWUD_Mount(std::string_view mountPath, std::string_view destination
// wua device
bool FSCDeviceWUA_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class ZArchiveReader* archive, sint32 priority);
// wuhb device
bool FSCDeviceWUHB_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class WUHBReader* wuhbReader, sint32 priority);
// hostFS device
void fscDeviceHostFS_mapBaseDirectories_deprecated();
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
// redirect device
void fscDeviceRedirect_map();
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority);
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority);

View file

@ -113,7 +113,7 @@ void FSCVirtualFile_Host::fscSetFileLength(uint64 endOffset)
m_fileSize = m_seek;
m_fs->SetPosition(m_seek);
if (!r)
cemuLog_force("fscSetFileLength: Failed to set size to 0x{:x}", endOffset);
cemuLog_log(LogType::Force, "fscSetFileLength: Failed to set size to 0x{:x}", endOffset);
}
bool FSCVirtualFile_Host::fscDirNext(FSCDirEntry* dirEntry)
@ -127,7 +127,7 @@ bool FSCVirtualFile_Host::fscDirNext(FSCDirEntry* dirEntry)
m_dirIterator.reset(new fs::directory_iterator(*m_path));
if (!m_dirIterator)
{
cemuLog_force("Failed to iterate directory: {}", _pathToUtf8(*m_path));
cemuLog_log(LogType::Force, "Failed to iterate directory: {}", _pathToUtf8(*m_path));
return false;
}
}
@ -175,14 +175,14 @@ FSCVirtualFile* FSCVirtualFile_Host::OpenFile(const fs::path& path, FSC_ACCESS_F
cemu_assert_debug(writeAccessRequested);
fs = FileStream::createFile2(path);
if (!fs)
cemuLog_force("FSC: File create failed for {}", _pathToUtf8(path));
cemuLog_log(LogType::Force, "FSC: File create failed for {}", _pathToUtf8(path));
}
}
else if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALWAYS_CREATE))
{
fs = FileStream::createFile2(path);
if (!fs)
cemuLog_force("FSC: File create failed for {}", _pathToUtf8(path));
cemuLog_log(LogType::Force, "FSC: File create failed for {}", _pathToUtf8(path));
}
else
{
@ -235,14 +235,14 @@ public:
if (fs::exists(dirPath))
{
if (!fs::is_directory(dirPath))
cemuLog_force("CreateDir: {} already exists but is not a directory", path);
cemuLog_log(LogType::Force, "CreateDir: {} already exists but is not a directory", path);
*fscStatus = FSC_STATUS_ALREADY_EXISTS;
return false;
}
std::error_code ec;
bool r = fs::create_directories(dirPath, ec);
if (!r)
cemuLog_force("CreateDir: Failed to create {}", path);
cemuLog_log(LogType::Force, "CreateDir: Failed to create {}", path);
*fscStatus = FSC_STATUS_OK;
return true;
}
@ -289,13 +289,6 @@ public:
}
};
void fscDeviceHostFS_mapBaseDirectories_deprecated()
{
const auto mlc = ActiveSettings::GetMlcPath();
fsc_mount("/cemuBossStorage/", _pathToUtf8(mlc / "usr/boss/"), &fscDeviceHostFSC::instance(), NULL, FSC_PRIORITY_BASE);
fsc_mount("/vol/storage_mlc01/", _pathToUtf8(mlc / ""), &fscDeviceHostFSC::instance(), NULL, FSC_PRIORITY_BASE);
}
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
{
return fsc_mount(mountPath, hostTargetPath, &fscDeviceHostFSC::instance(), nullptr, priority) == FSC_STATUS_OK;

View file

@ -11,7 +11,7 @@ struct RedirectEntry
FSAFileTree<RedirectEntry> redirectTree;
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority)
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority)
{
// check if source already has a redirection
RedirectEntry* existingEntry;
@ -24,7 +24,7 @@ void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& t
delete existingEntry;
}
RedirectEntry* entry = new RedirectEntry(targetFilePath, priority);
redirectTree.addFile(virtualSourcePath, entry);
redirectTree.addFile(virtualSourcePath, fileSize, entry);
}
class fscDeviceTypeRedirect : public fscDeviceC
@ -32,8 +32,15 @@ class fscDeviceTypeRedirect : public fscDeviceC
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
{
RedirectEntry* redirectionEntry;
if (redirectTree.getFile(path, redirectionEntry))
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE) && redirectTree.getFile(path, redirectionEntry))
return FSCVirtualFile_Host::OpenFile(redirectionEntry->dstPath, accessFlags, *fscStatus);
FSCVirtualFile* dirIterator;
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR) && redirectTree.getDirectory(path, dirIterator))
return dirIterator;
return nullptr;
}

View file

@ -128,7 +128,7 @@ class fscDeviceWUDC : public fscDeviceC
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
{
FSTFileHandle fstFileHandle;
if (mountedVolume->OpenFile(path, fstFileHandle, true))
if (mountedVolume->OpenFile(path, fstFileHandle, true) && !mountedVolume->HasLinkFlag(fstFileHandle))
{
*fscStatus = FSC_STATUS_OK;
return new FSCDeviceWudFileCtx(mountedVolume, fstFileHandle);
@ -137,7 +137,7 @@ class fscDeviceWUDC : public fscDeviceC
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
{
FSTDirectoryIterator dirIterator;
if (mountedVolume->OpenDirectoryIterator(path, dirIterator))
if (mountedVolume->OpenDirectoryIterator(path, dirIterator) && !mountedVolume->HasLinkFlag(dirIterator.GetDirHandle()))
{
*fscStatus = FSC_STATUS_OK;
return new FSCDeviceWudFileCtx(mountedVolume, dirIterator);

View file

@ -0,0 +1,151 @@
#include "Filesystem/WUHB/WUHBReader.h"
#include "Cafe/Filesystem/fsc.h"
#include "Cafe/Filesystem/FST/FST.h"
class FSCDeviceWuhbFileCtx : public FSCVirtualFile
{
public:
FSCDeviceWuhbFileCtx(WUHBReader* reader, uint32 entryOffset, uint32 fscType)
: m_wuhbReader(reader), m_entryOffset(entryOffset), m_fscType(fscType)
{
cemu_assert(entryOffset != ROMFS_ENTRY_EMPTY);
if (fscType == FSC_TYPE_DIRECTORY)
{
romfs_direntry_t entry = reader->GetDirEntry(entryOffset);
m_dirIterOffset = entry.dirListHead;
m_fileIterOffset = entry.fileListHead;
}
}
sint32 fscGetType() override
{
return m_fscType;
}
uint64 fscQueryValueU64(uint32 id) override
{
if (m_fscType == FSC_TYPE_FILE)
{
if (id == FSC_QUERY_SIZE)
return m_wuhbReader->GetFileSize(m_entryOffset);
else if (id == FSC_QUERY_WRITEABLE)
return 0; // WUHB images are read-only
else
cemu_assert_error();
}
else
{
cemu_assert_unimplemented();
}
return 0;
}
uint32 fscWriteData(void* buffer, uint32 size) override
{
cemu_assert_error();
return 0;
}
uint32 fscReadData(void* buffer, uint32 size) override
{
if (m_fscType != FSC_TYPE_FILE)
return 0;
auto read = m_wuhbReader->ReadFromFile(m_entryOffset, m_seek, size, buffer);
m_seek += read;
return read;
}
void fscSetSeek(uint64 seek) override
{
m_seek = seek;
}
uint64 fscGetSeek() override
{
if (m_fscType != FSC_TYPE_FILE)
return 0;
return m_seek;
}
void fscSetFileLength(uint64 endOffset) override
{
cemu_assert_error();
}
bool fscDirNext(FSCDirEntry* dirEntry) override
{
if (m_dirIterOffset != ROMFS_ENTRY_EMPTY)
{
romfs_direntry_t entry = m_wuhbReader->GetDirEntry(m_dirIterOffset);
m_dirIterOffset = entry.listNext;
if(entry.name_size > 0)
{
dirEntry->isDirectory = true;
dirEntry->isFile = false;
dirEntry->fileSize = 0;
std::strncpy(dirEntry->path, entry.name.c_str(), FSC_MAX_DIR_NAME_LENGTH);
return true;
}
}
if (m_fileIterOffset != ROMFS_ENTRY_EMPTY)
{
romfs_fentry_t entry = m_wuhbReader->GetFileEntry(m_fileIterOffset);
m_fileIterOffset = entry.listNext;
if(entry.name_size > 0)
{
dirEntry->isDirectory = false;
dirEntry->isFile = true;
dirEntry->fileSize = entry.size;
std::strncpy(dirEntry->path, entry.name.c_str(), FSC_MAX_DIR_NAME_LENGTH);
return true;
}
}
return false;
}
private:
WUHBReader* m_wuhbReader{};
uint32 m_fscType;
uint32 m_entryOffset = ROMFS_ENTRY_EMPTY;
uint32 m_dirIterOffset = ROMFS_ENTRY_EMPTY;
uint32 m_fileIterOffset = ROMFS_ENTRY_EMPTY;
uint64 m_seek = 0;
};
class fscDeviceWUHB : public fscDeviceC
{
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
{
WUHBReader* reader = (WUHBReader*)ctx;
cemu_assert_debug(!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION)); // writing to WUHB is not supported
bool isFile;
uint32 table_offset = ROMFS_ENTRY_EMPTY;
if (table_offset == ROMFS_ENTRY_EMPTY && HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
{
table_offset = reader->Lookup(path, false);
isFile = false;
}
if (table_offset == ROMFS_ENTRY_EMPTY && HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
{
table_offset = reader->Lookup(path, true);
isFile = true;
}
if (table_offset == ROMFS_ENTRY_EMPTY)
{
*fscStatus = FSC_STATUS_FILE_NOT_FOUND;
return nullptr;
}
*fscStatus = FSC_STATUS_OK;
return new FSCDeviceWuhbFileCtx(reader, table_offset, isFile ? FSC_TYPE_FILE : FSC_TYPE_DIRECTORY);
}
// singleton
public:
static fscDeviceWUHB& instance()
{
static fscDeviceWUHB _instance;
return _instance;
}
};
bool FSCDeviceWUHB_Mount(std::string_view mountPath, std::string_view destinationBaseDir, WUHBReader* wuhbReader, sint32 priority)
{
return fsc_mount(mountPath, destinationBaseDir, &fscDeviceWUHB::instance(), wuhbReader, priority) == FSC_STATUS_OK;
}

View file

@ -52,7 +52,7 @@ typedef struct
void hleExport_xcx_enterCriticalSection(PPCInterpreter_t* hCPU)
{
ppcDefineParamStructPtr(xcxCS, xcxCS_t, 0);
uint32 threadId = coreinitThread_getCurrentThreadMPTRDepr(hCPU);
uint32 threadId = MEMPTR<OSThread_t>(coreinit::OSGetCurrentThread()).GetMPTR();
cemu_assert_debug(xcxCS->ukn08 != 0);
cemu_assert_debug(threadId);
if (xcxCS->ownerThreadId == (uint32be)threadId)
@ -350,7 +350,7 @@ void GamePatch_scan()
hleAddr = hle_locate(ffl_floatArrayEndianSwap, NULL, sizeof(ffl_floatArrayEndianSwap));
if (hleAddr)
{
forceLogDebug_printf("HLE: Hook FFL float array endian swap function at 0x%08x", hleAddr);
cemuLog_logDebug(LogType::Force, "HLE: Hook FFL float array endian swap function at 0x{:08x}", hleAddr);
sint32 functionIndex = hleIndex_h000000003;
uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex
memory_write<uint32>(hleAddr, opcode);
@ -360,7 +360,7 @@ void GamePatch_scan()
//hleAddr = hle_locate(xcx_enterCriticalSectionSignature, xcx_enterCriticalSectionMask, sizeof(xcx_enterCriticalSectionSignature));
//if (hleAddr)
//{
// forceLogDebug_printf("HLE: Hook XCX enterCriticalSection function at 0x%08x", hleAddr);
// cemuLog_logDebug(LogType::Force, "HLE: Hook XCX enterCriticalSection function at 0x{:08x}", hleAddr);
// hleIndex_h000000004 = osLib_getFunctionIndex("hle", "h000000004");
// sint32 functionIndex = hleIndex_h000000004;
// uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex
@ -387,7 +387,7 @@ void GamePatch_scan()
hleAddr = hle_locate(smash4_softlockFixV0Signature, smash4_softlockFixV0Mask, sizeof(smash4_softlockFixV0Signature));
if (hleAddr)
{
forceLogDebug_printf("Smash softlock fix: 0x%08x", hleAddr);
cemuLog_logDebug(LogType::Force, "Smash softlock fix: 0x{:08x}", hleAddr);
memory_writeU32(hleAddr+0x20, memory_readU32(hleAddr+0x1C));
}
@ -396,7 +396,7 @@ void GamePatch_scan()
hleAddr = hle_locate(pmcs_yellowPaintStarCrashV0Signature, nullptr, sizeof(pmcs_yellowPaintStarCrashV0Signature));
if (hleAddr)
{
forceLogDebug_printf("Color Splash crash fix: 0x%08x", hleAddr);
cemuLog_logDebug(LogType::Force, "Color Splash crash fix: 0x{:08x}", hleAddr);
uint32 funcAddr = PPCInterpreter_makeCallableExportDepr(hleExport_pmcs_yellowPaintStarCrashWorkaround);
// set absolute jump
uint32 opc = 0x48000000;

View file

@ -43,7 +43,7 @@ bool gameProfile_loadBooleanOption(IniParser* iniParser, char* optionName, gameP
return true;
}
else
cemuLog_force("Unknown value '{}' for option '{}' in game profile", *option_value, optionName);
cemuLog_log(LogType::Force, "Unknown value '{}' for option '{}' in game profile", *option_value, optionName);
return false;
}
@ -64,7 +64,7 @@ bool gameProfile_loadBooleanOption2(IniParser& iniParser, const char* optionName
return true;
}
else
cemuLog_force("Unknown value '{}' for option '{}' in game profile", *option_value, optionName);
cemuLog_log(LogType::Force, "Unknown value '{}' for option '{}' in game profile", *option_value, optionName);
return false;
}
@ -94,7 +94,7 @@ bool gameProfile_loadIntegerOption(IniParser* iniParser, const char* optionName,
sint32 val = StringHelpers::ToInt(*option_value, defaultValue);
if (val < minVal || val > maxVal)
{
cemuLog_force("Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
cemuLog_log(LogType::Force, "Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
option->value = defaultValue;
return false;
}
@ -116,7 +116,7 @@ bool gameProfile_loadIntegerOption(IniParser& iniParser, const char* optionName,
T val = ConvertString<T>(*option_value);
if (val < minVal || val > maxVal)
{
cemuLog_force("Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
cemuLog_log(LogType::Force, "Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
return false;
}
@ -125,7 +125,7 @@ bool gameProfile_loadIntegerOption(IniParser& iniParser, const char* optionName,
}
catch(std::exception&)
{
cemuLog_force("Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
cemuLog_log(LogType::Force, "Value '{}' is out of range for option '{}' in game profile", *option_value, optionName);
return false;
}
}
@ -140,7 +140,7 @@ bool gameProfile_loadEnumOption(IniParser& iniParser, const char* optionName, T&
for(const T& v : T())
{
// test integer option
if (boost::iequals(fmt::format("{}", static_cast<typename std::underlying_type<T>::type>(v)), *option_value))
if (boost::iequals(fmt::format("{}", fmt::underlying(v)), *option_value))
{
option = v;
return true;
@ -175,7 +175,7 @@ void gameProfile_load()
ppcThreadQuantum = g_current_game_profile->GetThreadQuantum();
if (ppcThreadQuantum != GameProfile::kThreadQuantumDefault)
cemuLog_force("Thread quantum set to {}", ppcThreadQuantum);
cemuLog_log(LogType::Force, "Thread quantum set to {}", ppcThreadQuantum);
}
bool GameProfile::Load(uint64_t title_id)
@ -209,7 +209,7 @@ bool GameProfile::Load(uint64_t title_id)
m_gameName = std::string(game_name.begin(), game_name.end());
trim(m_gameName.value());
}
IniParser iniParser(*profileContents, gameProfilePath.string());
IniParser iniParser(*profileContents, _pathToUtf8(gameProfilePath));
// parse ini
while (iniParser.NextSection())
{
@ -283,7 +283,7 @@ void GameProfile::Save(uint64_t title_id)
FileStream* fs = FileStream::createFile2(gameProfilePath);
if (!fs)
{
cemuLog_force("Failed to write game profile");
cemuLog_log(LogType::Force, "Failed to write game profile");
return;
}

View file

@ -28,16 +28,16 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
return;
std::vector<uint8> rulesData;
fs_rules->extract(rulesData);
IniParser iniParser(rulesData, rulesPath.string());
IniParser iniParser(rulesData, _pathToUtf8(rulesPath));
if (!iniParser.NextSection())
{
cemuLog_force("{}: Does not contain any sections", _pathToUtf8(rulesPath));
cemuLog_log(LogType::Force, "{}: Does not contain any sections", _pathToUtf8(rulesPath));
return;
}
if (!boost::iequals(iniParser.GetCurrentSectionName(), "Definition"))
{
cemuLog_force("{}: [Definition] must be the first section", _pathToUtf8(rulesPath));
cemuLog_log(LogType::Force, "{}: [Definition] must be the first section", _pathToUtf8(rulesPath));
return;
}
@ -48,17 +48,16 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
auto [ptr, ec] = std::from_chars(option_version->data(), option_version->data() + option_version->size(), versionNum);
if (ec != std::errc{})
{
cemuLog_force("{}: Unable to parse version", _pathToUtf8(rulesPath));
cemuLog_log(LogType::Force, "{}: Unable to parse version", _pathToUtf8(rulesPath));
return;
}
if (versionNum > GP_LEGACY_VERSION)
{
GraphicPack2::LoadGraphicPack(rulesPath.generic_wstring(), iniParser);
GraphicPack2::LoadGraphicPack(rulesPath, iniParser);
return;
}
}
cemuLog_force("{}: Outdated graphic pack", _pathToUtf8(rulesPath));
cemuLog_log(LogType::Force, "{}: Outdated graphic pack", _pathToUtf8(rulesPath));
}
void GraphicPack2::LoadAll()
@ -79,22 +78,22 @@ void GraphicPack2::LoadAll()
}
}
bool GraphicPack2::LoadGraphicPack(const std::wstring& filename, IniParser& rules)
bool GraphicPack2::LoadGraphicPack(const fs::path& rulesPath, IniParser& rules)
{
try
{
auto gp = std::make_shared<GraphicPack2>(filename, rules);
auto gp = std::make_shared<GraphicPack2>(rulesPath, rules);
// check if enabled and preset set
const auto& config_entries = g_config.data().graphic_pack_entries;
// legacy absolute path checking for not breaking compatibility
auto file = gp->GetFilename2();
auto file = gp->GetRulesPath();
auto it = config_entries.find(file.lexically_normal());
if (it == config_entries.cend())
{
// check for relative path
it = config_entries.find(MakeRelativePath(ActiveSettings::GetUserDataPath(), gp->GetFilename2()).lexically_normal());
it = config_entries.find(_utf8ToPath(gp->GetNormalizedPathString()));
}
if (it != config_entries.cend())
@ -145,7 +144,7 @@ bool GraphicPack2::DeactivateGraphicPack(const std::shared_ptr<GraphicPack2>& gr
const auto it = std::find_if(s_active_graphic_packs.begin(), s_active_graphic_packs.end(),
[graphic_pack](const GraphicPackPtr& gp)
{
return gp->GetFilename() == graphic_pack->GetFilename();
return gp->GetNormalizedPathString() == graphic_pack->GetNormalizedPathString();
}
);
@ -173,12 +172,12 @@ void GraphicPack2::ActivateForCurrentTitle()
{
if (gp->GetPresets().empty())
{
cemuLog_log(LogType::Force, "Activate graphic pack: {}", gp->GetPath());
cemuLog_log(LogType::Force, "Activate graphic pack: {}", gp->GetVirtualPath());
}
else
{
std::string logLine;
logLine.assign(fmt::format("Activate graphic pack: {} [Presets: ", gp->GetPath()));
logLine.assign(fmt::format("Activate graphic pack: {} [Presets: ", gp->GetVirtualPath()));
bool isFirst = true;
for (auto& itr : gp->GetPresets())
{
@ -216,12 +215,6 @@ void GraphicPack2::WaitUntilReady()
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
GraphicPack2::GraphicPack2(std::wstring filename)
: m_filename(std::move(filename))
{
// unused for now
}
std::unordered_map<std::string, GraphicPack2::PresetVar> GraphicPack2::ParsePresetVars(IniParser& rules) const
{
ExpressionParser parser;
@ -255,8 +248,8 @@ std::unordered_map<std::string, GraphicPack2::PresetVar> GraphicPack2::ParsePres
return vars;
}
GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
: m_filename(std::move(filename))
GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
: m_rulesPath(std::move(rulesPath))
{
// we're already in [Definition]
auto option_version = rules.FindOption("version");
@ -265,7 +258,7 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
m_version = StringHelpers::ToInt(*option_version, -1);
if (m_version < 0)
{
cemuLog_force(L"{}: Invalid version", m_filename);
cemuLog_log(LogType::Force, "{}: Invalid version", _pathToUtf8(m_rulesPath));
throw std::exception();
}
@ -277,7 +270,7 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
else if (boost::iequals(*option_rendererFilter, "opengl"))
m_renderer_api = RendererAPI::OpenGL;
else
cemuLog_force("Unknown value '{}' for rendererFilter option", *option_rendererFilter);
cemuLog_log(LogType::Force, "Unknown value '{}' for rendererFilter option", *option_rendererFilter);
}
auto option_defaultEnabled = rules.FindOption("default");
@ -287,6 +280,10 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
m_enabled = m_default_enabled;
}
auto option_allowRendertargetSizeOptimization = rules.FindOption("colorbufferOptimizationAware");
if (option_allowRendertargetSizeOptimization)
m_allowRendertargetSizeOptimization = boost::iequals(*option_allowRendertargetSizeOptimization, "true") || boost::iequals(*option_allowRendertargetSizeOptimization, "1");
auto option_vendorFilter = rules.FindOption("vendorFilter");
if (option_vendorFilter)
{
@ -301,17 +298,17 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
else if (boost::iequals(*option_vendorFilter, "apple"))
m_gfx_vendor = GfxVendor::Apple;
else
cemuLog_force("Unknown value '{}' for vendorFilter", *option_vendorFilter);
cemuLog_log(LogType::Force, "Unknown value '{}' for vendorFilter", *option_vendorFilter);
}
auto option_path = rules.FindOption("path");
if (!option_path)
{
auto gp_name_log = rules.FindOption("name");
cemuLog_force("[Definition] section from '{}' graphic pack must contain option: path", gp_name_log.has_value() ? *gp_name_log : "Unknown");
cemuLog_log(LogType::Force, "[Definition] section from '{}' graphic pack must contain option: path", gp_name_log.has_value() ? *gp_name_log : "Unknown");
throw std::exception();
}
m_path = *option_path;
m_virtualPath = *option_path;
auto option_gp_name = rules.FindOption("name");
if (option_gp_name)
@ -348,7 +345,7 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
const auto preset_name = rules.FindOption("name");
if (!preset_name)
{
cemuLog_force("Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", m_name, rules.GetCurrentSectionLineNumber());
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", GetNormalizedPathString(), rules.GetCurrentSectionLineNumber());
continue;
}
@ -372,7 +369,7 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
}
catch (const std::exception & ex)
{
cemuLog_force("Graphic pack \"{}\": Can't parse preset \"{}\": {}", m_name, *preset_name, ex.what());
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Can't parse preset \"{}\": {}", GetNormalizedPathString(), *preset_name, ex.what());
}
}
else if (boost::iequals(currentSectionName, "RAM"))
@ -386,7 +383,7 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
{
if (m_version <= 5)
{
cemuLog_force("Graphic pack \"{}\": [RAM] options are only available for graphic pack version 6 or higher", m_name, optionNameBuf);
cemuLog_log(LogType::Force, "Graphic pack \"{}\": [RAM] options are only available for graphic pack version 6 or higher", GetNormalizedPathString(), optionNameBuf);
throw std::exception();
}
@ -396,12 +393,12 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
{
if (addrEnd <= addrStart)
{
cemuLog_force("Graphic pack \"{}\": start address (0x{:08x}) must be greater than end address (0x{:08x}) for {}", m_name, addrStart, addrEnd, optionNameBuf);
cemuLog_log(LogType::Force, "Graphic pack \"{}\": start address (0x{:08x}) must be greater than end address (0x{:08x}) for {}", GetNormalizedPathString(), addrStart, addrEnd, optionNameBuf);
throw std::exception();
}
else if ((addrStart & 0xFFF) != 0 || (addrEnd & 0xFFF) != 0)
{
cemuLog_force("Graphic pack \"{}\": addresses for %s are not aligned to 0x1000", m_name, optionNameBuf);
cemuLog_log(LogType::Force, "Graphic pack \"{}\": addresses for %s are not aligned to 0x1000", GetNormalizedPathString(), optionNameBuf);
throw std::exception();
}
else
@ -411,7 +408,7 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
}
else
{
cemuLog_force("Graphic pack \"{}\": has invalid syntax for option {}", m_name, optionNameBuf);
cemuLog_log(LogType::Force, "Graphic pack \"{}\": has invalid syntax for option {}", GetNormalizedPathString(), optionNameBuf);
throw std::exception();
}
}
@ -425,24 +422,32 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
std::unordered_map<std::string, std::vector<PresetPtr>> tmp_map;
// all vars must be defined in the default preset vars before
for (const auto& entry : m_presets)
std::vector<std::pair<std::string, std::string>> mismatchingPresetVars;
for (const auto& presetEntry : m_presets)
{
tmp_map[entry->category].emplace_back(entry);
tmp_map[presetEntry->category].emplace_back(presetEntry);
for (auto& kv : entry->variables)
for (auto& presetVar : presetEntry->variables)
{
const auto it = m_preset_vars.find(kv.first);
const auto it = m_preset_vars.find(presetVar.first);
if (it == m_preset_vars.cend())
{
cemuLog_force("Graphic pack: \"{}\" contains preset variables which are not defined in the default section", m_name);
throw std::exception();
mismatchingPresetVars.emplace_back(presetEntry->name, presetVar.first);
continue;
}
// overwrite var type with default var type
kv.second.first = it->second.first;
presetVar.second.first = it->second.first;
}
}
if(!mismatchingPresetVars.empty())
{
cemuLog_log(LogType::Force, "Graphic pack \"{}\" contains preset variables which are not defined in the [Default] section:", GetNormalizedPathString());
for (const auto& [presetName, varName] : mismatchingPresetVars)
cemuLog_log(LogType::Force, "Preset: {} Variable: {}", presetName, varName);
throw std::exception();
}
// have first entry be default active for every category if no default= is set
for(auto entry : get_values(tmp_map))
{
@ -472,7 +477,7 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
auto& p2 = kv.second[i + 1];
if (p1->variables.size() != p2->variables.size())
{
cemuLog_force("Graphic pack: \"{}\" contains inconsistent preset variables", m_name);
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", GetNormalizedPathString());
throw std::exception();
}
@ -480,14 +485,14 @@ GraphicPack2::GraphicPack2(std::wstring filename, IniParser& rules)
std::set<std::string> keys2(get_keys(p2->variables).begin(), get_keys(p2->variables).end());
if (keys1 != keys2)
{
cemuLog_force("Graphic pack: \"{}\" contains inconsistent preset variables", m_name);
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" contains inconsistent preset variables", GetNormalizedPathString());
throw std::exception();
}
if(p1->is_default)
{
if(has_default)
cemuLog_force("Graphic pack: \"{}\" has more than one preset with the default key set for the same category \"{}\"", m_name, p1->name);
cemuLog_log(LogType::Force, "Graphic pack: \"{}\" has more than one preset with the default key set for the same category \"{}\"", GetNormalizedPathString(), p1->name);
p1->active = true;
has_default = true;
}
@ -514,6 +519,11 @@ bool GraphicPack2::Reload()
return Activate();
}
std::string GraphicPack2::GetNormalizedPathString() const
{
return _pathToUtf8(MakeRelativePath(ActiveSettings::GetUserDataPath(), GetRulesPath()).lexically_normal());
}
bool GraphicPack2::ContainsTitleId(uint64_t title_id) const
{
const auto it = std::find_if(m_title_ids.begin(), m_title_ids.end(), [title_id](uint64 id) { return id == title_id; });
@ -656,7 +666,7 @@ bool GraphicPack2::SetActivePreset(std::string_view category, std::string_view n
void GraphicPack2::LoadShaders()
{
fs::path path(m_filename);
fs::path path = GetRulesPath();
for (auto& it : fs::directory_iterator(path.remove_filename()))
{
if (!is_regular_file(it))
@ -682,7 +692,7 @@ void GraphicPack2::LoadShaders()
{
std::ifstream file(p);
if (!file.is_open())
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename())));
file.seekg(0, std::ios::end);
m_output_shader_source.reserve(file.tellg());
@ -695,7 +705,7 @@ void GraphicPack2::LoadShaders()
{
std::ifstream file(p);
if (!file.is_open())
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename())));
file.seekg(0, std::ios::end);
m_upscaling_shader_source.reserve(file.tellg());
@ -708,7 +718,7 @@ void GraphicPack2::LoadShaders()
{
std::ifstream file(p);
if (!file.is_open())
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", p.filename().string()).c_str());
throw std::runtime_error(fmt::format("can't open graphic pack file: {}", _pathToUtf8(p.filename())));
file.seekg(0, std::ios::end);
m_downscaling_shader_source.reserve(file.tellg());
@ -811,7 +821,7 @@ void GraphicPack2::AddConstantsForCurrentPreset(ExpressionParser& ep)
}
}
void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, std::wstring& internalPath, bool isAOC)
void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC)
{
uint64 currentTitleId = CafeSystem::GetForegroundTitleId();
uint64 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull;
@ -828,7 +838,7 @@ void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, std::wstri
{
virtualMountPath = fs::path("vol/content/") / virtualMountPath;
}
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.path().generic_string(), m_fs_priority);
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.file_size(), it.path().generic_string(), m_fs_priority);
}
}
}
@ -839,7 +849,7 @@ void GraphicPack2::LoadReplacedFiles()
return;
m_patchedFilesLoaded = true;
fs::path gfxPackPath(m_filename.c_str());
fs::path gfxPackPath = GetRulesPath();
gfxPackPath = gfxPackPath.remove_filename();
// /content/
@ -849,10 +859,9 @@ void GraphicPack2::LoadReplacedFiles()
std::error_code ec;
if (fs::exists(contentPath, ec))
{
std::wstring internalPath(L"/vol/content/");
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(contentPath, internalPath, false);
_iterateReplacedFiles(contentPath, false);
}
// /aoc/
fs::path aocPath(gfxPackPath);
@ -863,13 +872,9 @@ void GraphicPack2::LoadReplacedFiles()
uint64 aocTitleId = CafeSystem::GetForegroundTitleId();
aocTitleId = aocTitleId & 0xFFFFFFFFULL;
aocTitleId |= 0x0005000c00000000ULL;
wchar_t internalAocPath[128];
swprintf(internalAocPath, sizeof(internalAocPath)/sizeof(wchar_t), L"/aoc/%016llx/", aocTitleId);
std::wstring internalPath(internalAocPath);
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(aocPath, internalPath, true);
_iterateReplacedFiles(aocPath, true);
}
}
@ -885,21 +890,18 @@ bool GraphicPack2::Activate()
if (m_gfx_vendor.has_value())
{
auto vendor = g_renderer->GetVendor();
if (vendor == GfxVendor::IntelLegacy || vendor == GfxVendor::IntelNoLegacy)
vendor = GfxVendor::Intel;
if (m_gfx_vendor.value() != vendor)
return false;
}
FileStream* fs_rules = FileStream::openFile2({ m_filename });
FileStream* fs_rules = FileStream::openFile2(m_rulesPath);
if (!fs_rules)
return false;
std::vector<uint8> rulesData;
fs_rules->extract(rulesData);
delete fs_rules;
IniParser rules({ (char*)rulesData.data(), rulesData.size()}, boost::nowide::narrow(m_filename));
IniParser rules({ (char*)rulesData.data(), rulesData.size()}, GetNormalizedPathString());
// load rules
try
@ -953,7 +955,7 @@ bool GraphicPack2::Activate()
else if (anisotropyValue == 16)
rule.overwrite_settings.anistropic_value = 4;
else
cemuLog_log(LogType::Force, fmt::format(L"Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, m_filename));
cemuLog_log(LogType::Force, "Invalid value {} for overwriteAnisotropy in graphic pack {}. Only the values 1, 2, 4, 8 or 16 are allowed.", anisotropyValue, GetNormalizedPathString());
}
m_texture_rules.emplace_back(rule);
}
@ -966,7 +968,7 @@ bool GraphicPack2::Activate()
auto option_upscale = rules.FindOption("upscaleMagFilter");
if(option_upscale && boost::iequals(*option_upscale, "NearestNeighbor"))
m_output_settings.upscale_filter = LatteTextureView::MagFilter::kNearestNeighbor;
auto option_downscale = rules.FindOption("NearestNeighbor");
auto option_downscale = rules.FindOption("downscaleMinFilter");
if (option_downscale && boost::iequals(*option_downscale, "NearestNeighbor"))
m_output_settings.downscale_filter = LatteTextureView::MagFilter::kNearestNeighbor;
}
@ -998,11 +1000,11 @@ bool GraphicPack2::Activate()
if (LatteTiming_getCustomVsyncFrequency(globalCustomVsyncFreq))
{
if (customVsyncFreq != globalCustomVsyncFreq)
cemuLog_log(LogType::Force, "rules.txt error: Mismatching vsync frequency {} in graphic pack \'{}\'", customVsyncFreq, GetPath());
cemuLog_log(LogType::Force, "rules.txt error: Mismatching vsync frequency {} in graphic pack \'{}\'", customVsyncFreq, GetVirtualPath());
}
else
{
cemuLog_log(LogType::Force, "Set vsync frequency to {} (graphic pack {})", customVsyncFreq, GetPath());
cemuLog_log(LogType::Force, "Set vsync frequency to {} (graphic pack {})", customVsyncFreq, GetVirtualPath());
LatteTiming_setCustomVsyncFrequency(customVsyncFreq);
}
}

View file

@ -97,23 +97,23 @@ public:
};
using PresetPtr = std::shared_ptr<Preset>;
GraphicPack2(std::wstring filename);
GraphicPack2(std::wstring filename, IniParser& rules);
GraphicPack2(fs::path rulesPath, IniParser& rules);
bool IsEnabled() const { return m_enabled; }
bool IsActivated() const { return m_activated; }
sint32 GetVersion() const { return m_version; }
const std::wstring& GetFilename() const { return m_filename; }
const fs::path GetFilename2() const { return fs::path(m_filename); }
const fs::path GetRulesPath() const { return m_rulesPath; }
std::string GetNormalizedPathString() const;
bool RequiresRestart(bool changeEnableState, bool changePreset);
bool Reload();
bool HasName() const { return !m_name.empty(); }
const std::string& GetName() const { return m_name.empty() ? m_path : m_name; }
const std::string& GetPath() const { return m_path; }
const std::string& GetName() const { return m_name.empty() ? m_virtualPath : m_name; }
const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy
const std::string& GetDescription() const { return m_description; }
bool IsDefaultEnabled() const { return m_default_enabled; }
bool AllowRendertargetSizeOptimization() const { return m_allowRendertargetSizeOptimization; }
void SetEnabled(bool state) { m_enabled = state; }
@ -165,7 +165,7 @@ public:
static const std::vector<std::shared_ptr<GraphicPack2>>& GetGraphicPacks() { return s_graphic_packs; }
static const std::vector<std::shared_ptr<GraphicPack2>>& GetActiveGraphicPacks() { return s_active_graphic_packs; }
static void LoadGraphicPack(fs::path graphicPackPath);
static bool LoadGraphicPack(const std::wstring& filename, class IniParser& rules);
static bool LoadGraphicPack(const fs::path& rulesPath, class IniParser& rules);
static bool ActivateGraphicPack(const std::shared_ptr<GraphicPack2>& graphic_pack);
static bool DeactivateGraphicPack(const std::shared_ptr<GraphicPack2>& graphic_pack);
static void ClearGraphicPacks();
@ -209,15 +209,17 @@ private:
parser.TryAddConstant(var.first, (TType)var.second.second);
}
std::wstring m_filename;
fs::path m_rulesPath;
sint32 m_version;
std::string m_name;
std::string m_path;
std::string m_virtualPath;
std::string m_description;
bool m_default_enabled = false;
bool m_allowRendertargetSizeOptimization = false; // gfx pack supports framebuffers with non-padded sizes, which is an optional optimization introduced with Cemu 2.0-74
// filter
std::optional<RendererAPI> m_renderer_api;
std::optional<GfxVendor> m_gfx_vendor;
@ -258,7 +260,7 @@ private:
CustomShader LoadShader(const fs::path& path, uint64 shader_base_hash, uint64 shader_aux_hash, GP_SHADER_TYPE shader_type) const;
void ApplyShaderPresets(std::string& shader_source) const;
void LoadReplacedFiles();
void _iterateReplacedFiles(const fs::path& currentPath, std::wstring& internalPath, bool isAOC);
void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC);
// ram mappings
std::vector<std::pair<MPTR, MPTR>> m_ramMappings;

View file

@ -6,6 +6,7 @@
#include "boost/algorithm/string.hpp"
#include "gui/wxgui.h" // for wxMessageBox
#include "gui/helpers/wxHelpers.h"
// error handler
void PatchErrorHandler::printError(class PatchGroup* patchGroup, sint32 lineNumber, std::string_view errorMsg)
@ -33,27 +34,29 @@ void PatchErrorHandler::printError(class PatchGroup* patchGroup, sint32 lineNumb
cemuLog_writeLineToLog(msg, true, true);
m_anyErrorTriggered = true;
if (cafeLog_isLoggingFlagEnabled(LOG_TYPE_PATCHES))
if (cemuLog_isLoggingEnabled(LogType::Patches))
errorMessages.emplace_back(msg);
}
void PatchErrorHandler::showStageErrorMessageBox()
{
std::string errorMsg;
wxString errorMsg;
if (m_gp)
{
if (m_stage == STAGE::PARSER)
errorMsg.assign(fmt::format("Failed to load patches for graphic pack \'{}\'", m_gp->GetName()));
errorMsg.assign(formatWxString(_("Failed to load patches for graphic pack \'{}\'"), m_gp->GetName()));
else
errorMsg.assign(fmt::format("Failed to apply patches for graphic pack \'{}\'", m_gp->GetName()));
errorMsg.assign(formatWxString(_("Failed to apply patches for graphic pack \'{}\'"), m_gp->GetName()));
}
else
{
cemu_assert_debug(false); // graphic pack should always be set
}
if (cafeLog_isLoggingFlagEnabled(LOG_TYPE_PATCHES))
if (cemuLog_isLoggingEnabled(LogType::Patches))
{
errorMsg.append("\n \nDetails:\n");
errorMsg.append("\n \n")
.append(_("Details:"))
.append("\n");
for (auto& itr : errorMessages)
{
errorMsg.append(itr);
@ -61,26 +64,15 @@ void PatchErrorHandler::showStageErrorMessageBox()
}
}
wxMessageBox(errorMsg, "Graphic pack error");
wxMessageBox(errorMsg, _("Graphic pack error"));
}
// loads Cemu-style patches (patch_<anything>.asm)
// returns true if at least one file was found even if it could not be successfully parsed
bool GraphicPack2::LoadCemuPatches()
{
// todo - once we have updated to C++20 we can replace these with the new std::string functions
auto startsWith = [](const std::wstring& str, const std::wstring& prefix)
{
return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix);
};
auto endsWith = [](const std::wstring& str, const std::wstring& suffix)
{
return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
};
bool foundPatches = false;
fs::path path(m_filename);
fs::path path(m_rulesPath);
path.remove_filename();
for (auto& p : fs::directory_iterator(path))
{
@ -88,10 +80,10 @@ bool GraphicPack2::LoadCemuPatches()
if (fs::is_regular_file(p.status()) && path.has_filename())
{
// check if filename matches
std::wstring filename = path.filename().generic_wstring();
if (boost::istarts_with(filename, L"patch_") && boost::iends_with(filename, L".asm"))
std::string filename = _pathToUtf8(path.filename());
if (boost::istarts_with(filename, "patch_") && boost::iends_with(filename, ".asm"))
{
FileStream* patchFile = FileStream::openFile(path.generic_wstring().c_str());
FileStream* patchFile = FileStream::openFile2(path);
if (patchFile)
{
// read file
@ -123,27 +115,20 @@ void GraphicPack2::LoadPatchFiles()
// order of loading patches:
// 1) Load Cemu-style patches (patch_<name>.asm), stop here if at least one patch file exists
// 2) Load Cemuhook patches.txt
// update: As of 1.20.2b Cemu always takes over patching since Cemuhook patching broke due to other internal changes (memory allocation changed and some reordering on when graphic packs get loaded)
if (LoadCemuPatches())
return; // exit if at least one Cemu style patch file was found
// fall back to Cemuhook patches.txt to guarantee backward compatibility
fs::path path(m_filename);
fs::path path(m_rulesPath);
path.remove_filename();
path.append("patches.txt");
FileStream* patchFile = FileStream::openFile(path.generic_wstring().c_str());
FileStream* patchFile = FileStream::openFile2(path);
if (patchFile == nullptr)
return;
// read file
std::vector<uint8> fileData;
patchFile->extract(fileData);
delete patchFile;
cemu_assert_debug(list_patchGroups.empty());
// parse
MemStreamReader patchesStream(fileData.data(), (sint32)fileData.size());
ParseCemuhookPatchesTxtInternal(patchesStream);

View file

@ -738,7 +738,7 @@ void GraphicPack2::UndoPatchGroups(std::vector<PatchGroup*>& groups, const RPLMo
void GraphicPack2::NotifyModuleLoaded(const RPLModule* rpl)
{
cemuLog_force("Loaded module \'{}\' with checksum 0x{:08x}", rpl->moduleName2, rpl->patchCRC);
cemuLog_log(LogType::Force, "Loaded module \'{}\' with checksum 0x{:08x}", rpl->moduleName2, rpl->patchCRC);
std::lock_guard<std::recursive_mutex> lock(mtx_patches);
list_modules.emplace_back(rpl);

View file

@ -25,7 +25,7 @@ sint32 GraphicPack2::GetLengthWithoutComment(const char* str, size_t length)
void GraphicPack2::LogPatchesSyntaxError(sint32 lineNumber, std::string_view errorMsg)
{
cemuLog_log(LogType::Force, fmt::format(L"Syntax error while parsing patch for graphic pack '{}':", this->GetFilename()));
cemuLog_log(LogType::Force, "Syntax error while parsing patch for graphic pack '{}':", _pathToUtf8(this->GetRulesPath()));
if(lineNumber >= 0)
cemuLog_log(LogType::Force, fmt::format("Line {0}: {1}", lineNumber, errorMsg));
else

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