Compare commits

..
Author SHA1 Message Date
56a115ca79
a bunch of bullshit
All checks were successful
Inkay-CI / build-inkay (push) Successful in 41s
2026-08-08 19:14:24 +02:00
cc7d9e8d84 Update src/lang/zh_CN.lang
Some checks failed
Inkay-CI / build-inkay (push) Failing after 24s
2026-08-08 02:45:12 +02:00
14501054ac Delete .github/workflows/codeql.yml
Some checks failed
Inkay-CI / build-inkay (push) Failing after 31s
2026-08-08 02:40:52 +02:00
dc1466eb36 fix ci
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Failing after 5s
CodeQL Advanced / Analyze (c-cpp) (push) Failing after 5s
Inkay-CI / build-inkay (push) Has been cancelled
2026-08-08 02:39:17 +02:00
bc2014fbe8 ci: fix for fj actions
Some checks failed
ci.yml / ci: fix for fj actions (push) Failing after 0s
CodeQL Advanced / Analyze (actions) (push) Failing after 14s
CodeQL Advanced / Analyze (c-cpp) (push) Failing after 6s
2026-08-08 02:36:04 +02:00
04bbcbfda9 fix language AGAIN 2026-08-08 02:25:26 +02:00
257d4ba119 fix language file issues 2026-08-08 02:17:48 +02:00
e14b6b69f1 merge with upstream inkay 2026-08-08 02:13:25 +02:00
e421ac5a40 malamar changes 2026-08-08 02:08:37 +02:00
Ash
5960ff1e40
Create codeql.yml 2026-07-29 21:38:46 +10:00
Ash Logan
890be941a9 Revert "Create codeql.yml"
This reverts commit 6df031b26d.
2026-07-29 21:33:03 +10:00
Ash
6df031b26d
Create codeql.yml 2026-07-29 21:28:40 +10:00
Ash Logan
10688b5ac2 fix(lang): include missing strings as blank 2026-07-28 22:54:16 +10:00
Ash Logan
df01b5b1dd feat(config): include line number in error reporting 2026-07-28 22:54:16 +10:00
Ash Logan
39dc7d8c8a fix: bad debug prints 2026-07-28 22:54:16 +10:00
Ash Logan
158c597aeb fix: shrink log data size slightly 2026-07-28 22:54:16 +10:00
Ash Logan
ccf5370f84 feat: switch config to C API and add missing strings
~40% bianry size reduction from RTTI/exceptions overhead
2026-07-28 22:54:16 +10:00
Ash Logan
e5020b88d0 fix: don't pull devoptab and sockets
requires new-ish aroma for LoggingModule i guess
2026-07-28 22:54:16 +10:00
Ash Logan
8cfbf64d54 chore: update toolchain 2026-07-28 22:54:16 +10:00
Ash
2df55177cb
Merge pull request #86 from PretendoNetwork/dependabot/github_actions/actions/checkout-7
chore: bump actions/checkout from 6 to 7
2026-07-27 20:36:28 +10:00
Daniel López Guimaraes
82cca21ff3
chore: Bump version to v3.1.0 2026-07-20 14:40:35 +01:00
d5f9207deb dont send splatoon patches if nn is enabled 2026-07-19 19:53:42 -04:00
1796ebebb5 OLV changes 2026-07-18 00:50:36 +02:00
dependabot[bot]
a43aed2456
chore: bump actions/checkout from 6 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 07:23:48 +00:00
998abca200 Update actions/checkout action to v7 2026-06-18 15:15:47 +00:00
Ash
1c795ea15b
Merge pull request #68 from ADev531/main
feat: added language changer and korean
2026-05-26 12:02:31 +10:00
4c432b753c chore: remove mentions of spfn.cc 2026-05-24 21:57:29 +02:00
ADev531
8d85d88e4e
Merge branch 'main' into main 2026-05-23 22:43:50 +09:00
Ash
b4407310e5
Merge pull request #85 from jazamb/show-startup-toast-patch
feat: make startup toast optional
2026-05-23 23:16:57 +10:00
Ash
1508e7c1e0
Merge pull request #81 from PretendoNetwork/weblate
Translations update from Hosted Weblate
2026-05-23 23:16:43 +10:00
Jas
b04ebb8624 feat: make startup toast optional
added a configuration option to show/hide startup toast
2026-05-21 14:00:39 -04:00
7403eaecd3 Update ghcr.io/wiiu-env/wiiumodulesystem Docker tag to v20250219 2026-05-18 20:00:48 +00:00
bf5336cba9 Add renovate.json 2026-05-18 21:50:27 +02:00
c2e1eef7e5 ci: downgrade to v3 2026-05-18 21:41:50 +02:00
d01d322003 ci: try to fix up to work (it prob wont) 2026-05-18 21:38:18 +02:00
00a75e94cb initial spfn commit 2026-05-16 22:18:39 +02:00
Valentijn
7bb99c30d1
Translated using Weblate (Dutch)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/nl/
2026-05-08 15:11:39 +00:00
Valentijn
cbe54f5467
Translated using Weblate (Dutch)
Currently translated at 84.6% (11 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/nl/
2026-05-08 15:11:38 +00:00
ADev531
52c877d2e8
Merge branch 'PretendoNetwork:main' into main 2026-03-21 07:22:50 +09:00
Ash
635f8b55ce
Merge pull request #83 from PretendoNetwork/dependabot/github_actions/actions/upload-artifact-7
chore: bump actions/upload-artifact from 6 to 7
2026-03-05 11:34:09 +11:00
dependabot[bot]
48ae2f97e0
chore: bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 07:28:53 +00:00
Ash
68afedb5ee
Merge pull request #71 from PretendoNetwork/weblate
Translations update from Hosted Weblate
2026-02-18 00:05:26 +11:00
kip
7be7a0040a
Translated using Weblate (Italian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/it/
2026-02-12 15:09:57 +01:00
BBasilio2001
9e90882ec8
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/zh_Hant/
2026-02-12 15:09:56 +01:00
Fefuchs
1f2660f2c1
Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/de/
2026-02-12 15:09:55 +01:00
Aaron Dewes
285d9f51fb
Translated using Weblate (German)
Currently translated at 92.3% (12 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/de/
2026-02-12 15:09:53 +01:00
adev531
a25a09cede fix: swap Italian and Spanish option on plugin settings 2026-01-29 13:45:00 +09:00
ADev531
d08482ae5d
Merge branch 'PretendoNetwork:main' into main 2025-12-18 21:29:58 +09:00
Ash
fd06e2f2aa
Merge pull request #78 from PretendoNetwork/dependabot/github_actions/actions/upload-artifact-6
chore: bump actions/upload-artifact from 5 to 6
2025-12-18 20:21:34 +11:00
dependabot[bot]
62c6cec406
chore: bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 07:25:29 +00:00
Ash
b7c86c9edc
Merge pull request #77 from PretendoNetwork/dependabot/github_actions/actions/checkout-6 2025-11-24 20:02:57 +11:00
dependabot[bot]
4c38d9e303
chore: bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 07:42:52 +00:00
Ash
c59e15395c
Merge pull request #76 from PretendoNetwork/dependabot/github_actions/actions/upload-artifact-5
chore: bump actions/upload-artifact from 4 to 5
2025-11-13 10:52:34 +11:00
dependabot[bot]
fe55961a18
chore: bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 08:07:17 +00:00
Ash
e3ff447fa6
Merge pull request #74 from PretendoNetwork/dependabot/github_actions/actions/checkout-5 2025-10-13 10:25:05 +11:00
Ash
257f7af9f8
Merge pull request #65 from PretendoNetwork/dependabot/docker/wiiu-env/devkitppc-20250608 2025-10-13 10:09:28 +11:00
dependabot[bot]
4c2cdd09d3
chore: bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-12 23:09:25 +00:00
Ash
c90d91cd62
Merge pull request #72 from ScottBrenner/patch-1 2025-10-13 10:08:47 +11:00
William Oldham
ca313e193e
Merge pull request #73 from ScottBrenner/alert-autofix-1
fix(CI): Code scanning alert: Workflow does not contain permissions
2025-10-12 10:23:47 +01:00
Scott Brenner
9fa4beae2e
Potential fix for code scanning alert no. 1: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-10-11 19:34:49 -07:00
Scott Brenner
79bc63adb8
feat(CI): Extend Dependabot workflow for GitHub Actions 2025-10-11 19:29:26 -07:00
ADev531
83790b26bf
Merge branch 'PretendoNetwork:main' into main 2025-09-16 21:55:58 +09:00
Ash
bda6bc0d32
Merge pull request #70 from PretendoNetwork/weblate
Translations update from Hosted Weblate
2025-09-16 11:45:40 +10:00
Dimitri A
cfd4c01896
Translated using Weblate (French)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/fr/
2025-09-15 23:01:58 +02:00
Apueblar
eb26bc32d8
Translated using Weblate (Spanish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/
2025-09-11 00:02:02 +00:00
Dimitri A
d0985dd17a
Translated using Weblate (Italian)
Currently translated at 84.6% (11 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/it/
2025-08-22 22:02:22 +02:00
Arnau Bàrcia
0b0fbfc742
Translated using Weblate (Spanish)
Currently translated at 92.3% (12 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/
2025-08-21 13:02:24 +00:00
Ash
f1b725867a
Merge pull request #69 from PretendoNetwork/weblate
Translations update from Hosted Weblate
2025-08-07 20:07:49 +10:00
Arnau Bàrcia
4ad4666272
Translated using Weblate (Spanish)
Currently translated at 84.6% (11 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/
2025-08-06 03:02:43 +02:00
ADev531
6e1122e1b3 fix: added Korean and UwU to language changer 2025-07-21 21:15:09 +09:00
ADev531
907de64376 feat: added language changing 2025-07-21 20:59:55 +09:00
ADev531
979d9de91a feat: added korean 2025-07-21 20:58:20 +09:00
Ash
8da5fde962
Merge pull request #67 from PretendoNetwork/weblate
Translations update from Hosted Weblate
2025-07-02 15:58:50 +10:00
Ash Tails
194dc90f59
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 76.9% (10 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/zh_Hant/
2025-07-02 05:16:34 +02:00
Ash Tails
3647725527
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 76.9% (10 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/zh_Hant/
2025-07-02 05:16:33 +02:00
Ash Tails
f3522fb5ac
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/zh_Hans/
2025-07-02 05:16:31 +02:00
Ash Tails
e62f921d71
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/zh_Hans/
2025-07-02 05:16:30 +02:00
Maxime Tourneur
d1b5ba6113
Translated using Weblate (French)
Currently translated at 84.6% (11 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/fr/
2025-07-02 05:16:26 +02:00
Ash Tails
889794f239
Translated using Weblate (English)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/en/
2025-07-02 05:16:24 +02:00
Chris
724060d1c7
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 92.3% (12 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/zh_Hant/
2025-06-30 18:24:15 +02:00
Ash Tails
6a54c86d06
Translated using Weblate (English)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/en/
2025-06-30 18:24:15 +02:00
Chris
2db5735085
Translated using Weblate (English)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/en/
2025-06-30 18:24:14 +02:00
Ash Logan
ee49400d55 Merge remote-tracking branch 'weblate/main' 2025-06-30 18:02:07 +10:00
SMU8
c1430e5a8c
Translated using Weblate (Dutch)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/nl/
2025-06-10 15:03:02 +02:00
dependabot[bot]
ea41e73d8d
chore: bump wiiu-env/devkitppc from 20241128 to 20250608
Bumps wiiu-env/devkitppc from 20241128 to 20250608.

---
updated-dependencies:
- dependency-name: wiiu-env/devkitppc
  dependency-version: '20250608'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 07:39:05 +00:00
Daniel López Guimaraes
add1d2c2e2
chore: Bump to version 3.0.0 2025-02-15 21:09:10 +00:00
Daniel López Guimaraes
8bc8ac0290
Merge pull request #61 from Maschell/notifications 2025-02-15 12:40:44 +00:00
Daniel López Guimaraes
4cf3b7446e
chore: Add fallback translations to English 2025-02-15 12:38:27 +00:00
Daniel López Guimaraes
93f9a3dff4
Merge pull request #54 from weblate/weblate-pretendonetwork-inkay 2025-02-15 12:25:47 +00:00
Daniel López Guimaraes
97167e0dab
fix(lang): Fix translation entries order 2025-02-15 12:18:50 +00:00
Maschell
4a880c1a2e feat: Check title version before doing game specific patches 2025-02-09 10:39:14 +01:00
Maschell
79ccad1fd4 fix: Do title specific patches more than once 2025-02-09 10:39:08 +01:00
Maschell
ed75702ac5 Review fixes and update dockerfile 2025-02-08 23:05:25 +01:00
Maschell
429d55ba9f
Apply suggestions from code review
Co-authored-by: Daniel López Guimaraes <112760654+DaniElectra@users.noreply.github.com>
2025-02-03 19:38:44 +01:00
Maschell
e2cf529b3f fix: Only allow the plugin to init Inkay at boot to avoid potential crashes 2025-02-03 12:45:17 +01:00
Maschell
24f680c6fb fix: Only do function patches if the plugin had to to update init Inkay 2025-02-03 12:44:29 +01:00
kaio mesquita fragoso
f9b0477c4e
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/pt_BR/
2025-02-02 19:00:08 +01:00
Maschell
cc3fac1cc7 (chore): clean up includes 2025-01-30 18:27:20 +01:00
Maschell
1ffb9cf65a feat: Show notification is plugin is missing or module was never initialized 2025-01-30 18:26:01 +01:00
Deleted User
f6bb2805f6
Translated using Weblate (Spanish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/
2025-01-30 09:24:23 +01:00
Vitaly Novichkov
eef3580fca
Translated using Weblate (Russian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/ru/
2025-01-25 00:01:46 +01:00
Lacey Anaya
b5cf02bb90
Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/de/
2025-01-22 23:02:33 +01:00
a9ee61babe
Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/de/
2025-01-21 22:49:11 +01:00
Lacey Anaya
1acb60ff49
Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/de/
2025-01-21 22:49:11 +01:00
0afd60d7f6
Translated using Weblate (German)
Currently translated at 92.3% (12 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/de/
2025-01-21 22:48:21 +01:00
almi05
f02eacad88
Translated using Weblate (Italian)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/it/
2025-01-19 13:00:41 +01:00
Alex Wang
4adf1f267d
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/zh_Hans/
2025-01-03 07:02:17 +01:00
Eduardo Toebe
a6e1fae402
Translated using Weblate (Portuguese (Brazil))
Currently translated at 92.3% (12 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/pt_BR/
2024-12-24 06:01:08 +00:00
Ellie Rosa Maiella
816b34473b
Translated using Weblate (Italian)
Currently translated at 76.9% (10 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/it/
2024-12-24 06:01:08 +00:00
Dimitri A
b4c4cf424b
Translated using Weblate (Japanese)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/ja/
2024-12-22 16:00:36 +01:00
Dimitri A
bb34ab259c
Translated using Weblate (Japanese)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/ja/
2024-12-21 15:23:12 +01:00
Ash Tails
e6fd32cf7a
Translated using Weblate (Japanese)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/ja/
2024-12-21 15:23:12 +01:00
Dimitri A
85e64547d3
Translated using Weblate (Japanese)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/ja/
2024-12-21 15:23:12 +01:00
Dimitri A
4c4f8ae355
Translated using Weblate (French)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/fr/
2024-12-21 15:23:12 +01:00
mathijn wismeijer
f5e136704b
Translated using Weblate (Dutch)
Currently translated at 92.3% (12 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/nl/
2024-12-21 15:23:12 +01:00
Arnau Bàrcia
5b4fbd0521
Translated using Weblate (Spanish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/
2024-12-21 15:23:12 +01:00
Dimitri A
3a79f3228f
Translated using Weblate (French)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/fr/
2024-12-21 15:23:12 +01:00
Dimitri A
abc2e53c6e
Translated using Weblate (French)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/fr/
2024-12-21 15:23:12 +01:00
Daniel López Guimaraes
08fa9ec269
Translated using Weblate (Spanish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/
2024-12-21 15:23:12 +01:00
Ash
fa59526cd0
docs: Add some basic compilation instructions 2024-12-21 23:17:05 +11:00
Ash
eea33cbd18
chore: Run CI builds for pull requests too 2024-12-21 22:42:05 +11:00
Ash
4966ef3eca
Merge pull request #58 from PretendoNetwork/dependabot/docker/wiiu-env/devkitppc-20241128
chore: bump wiiu-env/devkitppc from 20240505 to 20241128
2024-12-21 22:36:42 +11:00
dependabot[bot]
cf6ad91733
chore: bump wiiu-env/devkitppc from 20240505 to 20241128
Bumps wiiu-env/devkitppc from 20240505 to 20241128.

---
updated-dependencies:
- dependency-name: wiiu-env/devkitppc
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-21 11:35:09 +00:00
Ash
e1d7cb56c2
Create dependabot.yml 2024-12-21 22:34:35 +11:00
Ash Logan
9e376f7839 feat: Move all URL defines into a central location 2024-12-17 16:44:59 +11:00
Ash Logan
38f1b1848e fix(account-settings): Don't re-add function patches on every application boot
libfunctionpatcher stuff is persistent from plugin init time to plugin teardown time. but the memory replacements DO need to happen on every launch, so rearrange things a bit.
2024-12-17 15:13:39 +11:00
Ash
23728c4288
Merge pull request #56 from DimitriPilot3/fix-account-settings-patch
fix(account_settings): Avoid mixing up FSReadFile / FSCloseFile patches
2024-12-15 11:16:59 +11:00
Dimitri A.
6c78a95872 fix(account_settings): Avoid mixing up FSReadFile / FSCloseFile patches
This copy-and-paste error was a culprit of the bug described in issue #55.
It made the Account Settings applet on Wii U softlock during its initialisation, forcing the user to power off the system.

The applet now works again. However, trying to access it a second time without rebooting still freezes the Wii U, which is another regression listed in issue #55.
2024-12-15 00:29:27 +01:00
Ash
ebecf957f1
Merge pull request #53 from weblate/weblate-pretendonetwork-inkay
Translations update from Hosted Weblate
2024-12-01 00:26:01 +11:00
Ash Tails
8d7df27d42
Translated using Weblate (English (en@uwu))
Currently translated at 100.0% (13 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/en@uwu/
2024-11-30 10:11:52 +01:00
redmine4404
0f1e05b021
Translated using Weblate (French)
Currently translated at 84.6% (11 of 13 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/fr/
2024-11-30 10:11:51 +01:00
Ash Tails
2510c691f1
Added translation using Weblate (English (en@uwu)) 2024-11-30 10:11:51 +01:00
Ash Tails
4009d44a97
Deleted translation using Weblate (English (en@uwu)) 2024-11-30 10:11:50 +01:00
Ash Tails
e6edcec5a5
Added translation using Weblate (English (en@uwu)) 2024-11-30 10:11:50 +01:00
Ash Logan
a6b91be935 feat(p2p): Port overrides for Splatoon and MK8 2024-11-29 21:40:21 +11:00
Ash Logan
727f11d04d feat: Compile flags for binary size
Small binaries load faster, work better on Aroma Updater, and break the website less
2024-11-29 21:40:21 +11:00
Ash Logan
dbb648729a fix(config): Use noexcept version of WupsConfigItemStub::create 2024-11-29 21:40:21 +11:00
Ash
4b0e298b8e
Update README.md 2024-11-25 18:24:29 +11:00
Ash Logan
cd1d530da9 fix(config): Switch to snprintf for formatting
while vformat is closer to modern string-formatting sensibilities, it added 400KiB to our binaries, whereas snprintf has no noticeable impact. so... suffer, and hope the translators know what's up
2024-11-22 11:44:20 +11:00
Ash
2bf5f8bd18
Merge pull request #50 from DaniElectra/module 2024-11-22 09:44:10 +11:00
Daniel López Guimaraes
0f3115a402
fix(p2p): Don't apply patches when not using Pretendo 2024-11-21 20:43:17 +00:00
Daniel López Guimaraes
06847b21e5
fix(p2p): Fix compilation error in multiplayer port text
GCC complains about std::make_format_args using non-const references, so
we can't pass along the `get_console_peertopeer_port` directly.

Also replace `unsigned short` with `uint16_t` for clarity.
2024-11-21 20:39:51 +00:00
Ash Logan
a710bd985a feat(lang): Tell users how to fix module-related errors
Realistically, 99% of people will not know how to "ensure the inkay module is properly installed" - Aroma updater is designed to automatically fix random stuff like this
It hashes every individual file so it will pick up SD card corruption, partial installs, etc
2024-11-21 13:49:54 +11:00
Ash Logan
ea71879bb1 chore: Introduce common directory to deduplicate shared code 2024-11-21 13:19:06 +11:00
Daniel López Guimaraes
68bc066c7b feat: Add translation for module not found errors 2024-11-21 12:50:38 +11:00
Daniel López Guimaraes
7958373394 feat: Improvements in module exports
Add new `Inkay_GetStatus` which can be used for retrieving the status of
the module. Also add better handling of module initialization, and show
a notification from the plugin if the module can't be found.
2024-11-21 12:50:23 +11:00
Daniel López Guimaraes
d3ff378cc4 chore: Remove unnecesary file
`wut_extra.h` is only used in the plugin, not in the module.
2024-11-21 12:49:19 +11:00
Daniel López Guimaraes
3460feeeaf fix(Makefile): Replicate original compiler settings 2024-11-21 12:49:19 +11:00
Daniel López Guimaraes
f4aeee8034 feat!: Split Inkay into a module and a plugin
This is necessary for a future Aroma feature. This also makes more clear
the immutability of the patches that Pretendo requires, since they
cannot be reverted easily.

All patching code is now managed inside the module, which the plugin
triggers by calling an exported function from it. The WWP reset code
remains within the plugin as it isn't strictly tied to the patches.
2024-11-21 12:49:16 +11:00
Ash Logan
cc967126fe feat(p2p): Show p2p port in the config menu
with love to my weblate people
2024-11-17 23:59:26 +11:00
Ash Logan
22bebff027 feat(p2p): Allow overriding the p2p udp port for Minecraft
the idea is that you might port forward this in your router and give yourself NAT type A for free
2024-11-17 23:59:26 +11:00
Weblate (bot)
a1375ca608
Translations update from Hosted Weblate (#52)
* Translated using Weblate (Spanish)

Currently translated at 100.0% (10 of 10 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (10 of 10 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/

---------

Co-authored-by: Arnau Bàrcia <a.barcia.sallegi@gmail.com>
2024-11-06 00:41:54 +11:00
Weblate (bot)
9d29ad78fd
Translated using Weblate (Spanish) (#47)
Currently translated at 100.0% (10 of 10 strings)

Translation: Pretendo Network/Inkay
Translate-URL: https://hosted.weblate.org/projects/pretendonetwork/inkay/es/

Co-authored-by: gallegonovato <fran-carro@hotmail.es>
2024-10-06 13:33:51 +11:00
Ash Logan
6baee13685 feat(i18n): Move all texts into .lang files which are actually Java properties and C structs at the same time
no further comment
2024-10-03 00:00:56 +10:00
Ash Logan
1d1e094257 feat(i18n): Move NN/PN messages to main config_strings struct 2024-10-02 23:46:35 +10:00
Ash Logan
b16237417c fix(olv): Allow RPLs with null names
CafeGLSL is an offender here
2024-09-11 13:07:29 +10:00
Ash Logan
aedd02b0d1 chore(config): Switch to string_view for translated strings
Elides about 100 strlen calls by allowing the length to be calculated at compile time

ofc the WUPS backend doesn't USE this info but, y'know.
2024-08-04 18:15:26 +10:00
Ash Logan
c6d80bd550 chore(config): Refactor error handling and remove exceptions
Honestly kinda feeling like the C++ API isn't worth it
2024-08-04 17:44:24 +10:00
Ash Logan
1a85a45ce3 chore: Tighten up logspam and binary size
Remove some old stuff to Make Small Inkay
2024-08-04 16:24:01 +10:00
yarb00
04d723137b feat(i18n): Add Russian translation
Closes #41

Co-authored-by: Ash Logan <ash@heyquark.com>
2024-08-03 22:05:28 +10:00
JouriR
956419a438 feat(i18n): Add Dutch translation
Closes #36

Co-authored-by: Ash Logan <ash@heyquark.com>
2024-08-03 22:00:50 +10:00
Dekokiyo
bdfb6ca859 feat(i18n): Add Japanese translation
Closes #33

Co-authored-by: Ash Logan <ash@heyquark.com>
2024-08-03 21:59:02 +10:00
Luís
a6fd3783f1 feat(i18n): Add Portuguese translation
Closes #31

Co-authored-by: Ash Logan <ash@heyquark.com>
2024-08-03 21:57:15 +10:00
一时 三月
867ea12f17 feat(i18n): Add Traditional Chinese, Simplified Chinese
Closes #25

Co-authored-by: Ash Logan <ash@heyquark.com>
2024-08-03 21:54:16 +10:00
Ash Logan
734e5d40c9 chore: clean up old includes
caused compile issues on latest tools - the curl include is old and the wut headers added C++ templates
2024-08-03 21:46:46 +10:00
60 changed files with 2210 additions and 613 deletions

View file

@ -1,18 +1,22 @@
name: Inkay-CI
permissions:
contents: read
on: push
on:
- push
- pull_request
jobs:
build-inkay:
runs-on: ubuntu-22.04
runs-on: debian-trixie
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v7
- name: build toolchain container
run: docker build . -t builder
- uses: ammaraskar/gcc-problem-matcher@master
- uses: https://github.com/ammaraskar/gcc-problem-matcher@master
- name: build Inkay
run: docker run --rm -v ${PWD}:/app -w /app builder
- uses: actions/upload-artifact@master
- uses: actions/upload-artifact@v3
with:
name: inkay
path: "*.wps"
path: dist/

7
.gitignore vendored
View file

@ -1,7 +1,12 @@
.cache/
.vscode/
.idea/
/build
/plugin/build
/dist
*.elf
*.wms
*.wps
certs/
*.lst
*.lst
inkay_config.local.h

View file

@ -1,10 +1,11 @@
FROM ghcr.io/wiiu-env/devkitppc:20240505
FROM ghcr.io/wiiu-env/devkitppc:20260504
COPY --from=ghcr.io/wiiu-env/libnotifications:20240426 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libkernel:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmocha:20231127 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20250219 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20240505 /artifacts $DEVKITPRO
WORKDIR /app
CMD make -f Makefile -j$(nproc)
CMD make -f Makefile -j$(nproc)

View file

@ -8,10 +8,10 @@ endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/wups/share/wups_rules
include $(DEVKITPRO)/wums/share/wums_rules
WUT_ROOT := $(DEVKITPRO)/wut
WUMS_ROOT := $(DEVKITPRO)/wums
WUT_ROOT := $(DEVKITPRO)/wut
#-------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
@ -19,39 +19,38 @@ WUMS_ROOT := $(DEVKITPRO)/wums
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#-------------------------------------------------------------------------------
TARGET := Inkay-pretendo
TARGET := Malamar-spfn
BUILD := build
SOURCES := src src/patches src/utils src/ext/inih
SOURCES := src src/patches src/utils src/ext/inih common
DATA := data
INCLUDES := src src/ext/inih
#DEBUG := 1
INCLUDES := src src/ext/inih src/lang common
#-------------------------------------------------------------------------------
# options for code generation
#-------------------------------------------------------------------------------
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(MACHDEP)
OPT := -Os -fno-exceptions -fno-asynchronous-unwind-tables
CFLAGS := -Wall -ffunction-sections -fdata-sections \
$(MACHDEP) $(OPT)
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
ifeq ($(DEBUG),1)
CFLAGS += -DDEBUG
endif
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
CXXFLAGS := $(CFLAGS) -std=c++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
LDFLAGS = -g $(ARCH) $(OPT) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -Wl,-gc-sections -T$(WUMS_ROOT)/share/libkernel.ld $(WUMSSPECS)
LDFLAGS += -T$(WUMS_ROOT)/share/libkernel.ld $(WUPSSPECS)
ifeq ($(DEBUG),1)
CXXFLAGS += -DDEBUG -g
CFLAGS += -DDEBUG -g
endif
LIBS := -lwups -lmocha -lkernel -lwut -lnotifications -lfunctionpatcher
LIBS := -lwums -lmocha -lkernel -lwut -lfunctionpatcher -lnotifications
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
# containing include and lib
#-------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUPS_ROOT) $(WUT_ROOT) $(WUT_ROOT)/usr
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) $(WUT_ROOT)/usr
#-------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
@ -103,14 +102,20 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
#-------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@$(MAKE) --no-print-directory -C $(CURDIR)/plugin -f $(CURDIR)/plugin/Makefile
mkdir -p dist/
cp *.wms dist/
cp plugin/*.wps dist/
#-------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf
@rm -fr dist $(BUILD) $(TARGET).wms $(TARGET).elf
@$(MAKE) --no-print-directory -C $(CURDIR)/plugin -f $(CURDIR)/plugin/Makefile clean
#-------------------------------------------------------------------------------
else
@ -121,10 +126,10 @@ DEPENDS := $(OFILES:.o=.d)
#-------------------------------------------------------------------------------
# main targets
#-------------------------------------------------------------------------------
all : $(OUTPUT).wps
all : $(OUTPUT).wms
$(OUTPUT).wps : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OUTPUT).wms : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
@ -141,6 +146,11 @@ $(OFILES_SRC) : $(HFILES_BIN)
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
%.o: %.s
@echo $(notdir $<)
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
-include $(DEPENDS)
#-------------------------------------------------------------------------------

View file

@ -1,16 +1,40 @@
# Inkay - Aroma patches for Pretendo
# Malamar - Aroma patches for Splatfestival Network/Spacebar
[![Pretendo network logo](https://github.com/PretendoNetwork/website/raw/master/public/assets/images/opengraph/opengraph-image.png)](https://pretendo.network)
Malamar is an Aroma/WUPS plugin based on Pretendo's Malamar that patches various Nintendo Network URLs on a Wii U to use Splatfestival/Spacebar Network instead. It also (for the time being) bypasses SSL verification in most cases. It redirects Nintendo Network in:
Inkay is an Aroma/WUPS plugin that patches various Nintendo Network URLs on a Wii U to use Pretendo Network instead. It also (for the time being) bypasses SSL verification in most cases.
- IOSU-side connections (Friends, SpotPass, accounts etc.)
- Account Settings
- NNCS
- Nintendo eShop
- Miiverse (in-game)
- Miiverse applet
Inkay does not currently include the game-specific patches present in [Nimble](https://github.com/PretendoNetwork/Nimble). These will be implemented soon™.
Malamar also includes game-specific patches to add extra features:
- Modpack-specific matchmaking for global, regional rooms (by simulating extra DLC) - **Mario Kart 8**
- P2P port override for better connection stability (if you port forward) - **Minecraft: Wii U Edition**, **Mario Kart 8**, **Splatoon**
## Dependencies
Inkay is only supported on the release version of Aroma configured for autoboot/coldboot. For Tiramisu, see [Nimble](https://github.com/PretendoNetwork/Nimble).
## Requirements
Malamar is only supported on the release version of Aroma configured for autoboot/coldboot. Other configurations (specifically lacking coldboot) may cause issues with SpotPass.
## Safety
Inkay's patches are all temporary, and only applied in-memory without modifying your console. The SSL patch, while also temporary, could reduce the overall security of your console while active - this is because it no longer checks if a server is verified. However, this does not apply to the Internet Browser, where SSL still works as expected.
Malamar's patches are all temporary, and only applied in-memory without modifying your console. The SSL patch, while also temporary, could reduce the overall security of your console while active - this is because it no longer checks if a server is verified. However, this does not apply to the Internet Browser, where SSL still works as expected.
## TODO
See [Issues](https://github.com/PretendoNetwork/Inkay/issues).
## Compiling - Docker
Malamar's dependencies and build tooling can be handled as a container, which is recommended for WUPS plugins. Using `docker` or `podman`:
```shell
docker build -t malamar .
docker run --rm -v $(pwd):/app malamar make
# you can replace "make" with other commands - e.g. make clean
```
If using `podman` on SELinux systems (like Fedora Linux), you might need to use `$(pwd):/app:Z` instead of `$(pwd):/app`.
## Compiling - System
Malamar has the following dependencies aside from devkitPPC and wut:
- [WiiUPluginSystem](https://github.com/wiiu-env/WiiUPluginSystem)
- [WiiUModuleSystem](https://github.com/wiiu-env/WiiUModuleSystem)
- [libmocha](https://github.com/wiiu-env/libmocha)
- [libkernel](https://github.com/wiiu-env/libkernel/)
- [libnotifications](https://github.com/wiiu-env/libnotifications/)
- [libfunctionpatcher](https://github.com/wiiu-env/libfunctionpatcher)
Each of these should be `make install`-able. After that, you can compile Malamar with `make`.

View file

@ -18,7 +18,7 @@
#include <notifications/notification_defines.h>
#include <notifications/notifications.h>
void ShowNotification(std::string_view notification) {
void ShowNotification(const char* notification) {
auto err1 = NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN, true);
auto err2 = NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO,
@ -27,5 +27,5 @@ void ShowNotification(std::string_view notification) {
if (err1 != NOTIFICATION_MODULE_RESULT_SUCCESS || err2 != NOTIFICATION_MODULE_RESULT_SUCCESS) return;
NotificationModule_AddInfoNotification(notification.data());
NotificationModule_AddInfoNotification(notification);
}

3
common/Notification.h Normal file
View file

@ -0,0 +1,3 @@
#pragma once
void ShowNotification(const char* notification);

21
common/inkay_config.h Normal file
View file

@ -0,0 +1,21 @@
//
// Created by ash on 17/12/24.
//
#ifndef INKAY_INKAY_CONFIG_H
#define INKAY_INKAY_CONFIG_H
#ifdef __has_include
#if __has_include("inkay_config.local.h")
#include "inkay_config.local.h"
#define INKAY_CUSTOM 1
#endif
#endif
#ifndef NETWORK_BASEURL
#define NETWORK_BASEURL "spbr.net"
#endif
#endif //INKAY_INKAY_CONFIG_H

47
common/lang.cpp Normal file
View file

@ -0,0 +1,47 @@
//
// Created by ash on 21/11/24.
//
#include "lang.h"
config_strings get_config_strings(inkay_language language) {
switch (language) {
case inkay_language::English:
default: return {
#include "en_US.lang"
};
case inkay_language::Spanish: return {
#include "es_ES.lang"
};
case inkay_language::French: return {
#include "fr_FR.lang"
};
case inkay_language::Italian: return {
#include "it_IT.lang"
};
case inkay_language::German: return {
#include "de_DE.lang"
};
case inkay_language::SimplifiedChinese: return {
#include "zh_CN.lang"
};
case inkay_language::Portuguese: return {
#include "pt_BR.lang"
};
case inkay_language::Japanese: return {
#include "ja_JP.lang"
};
case inkay_language::Dutch: return {
#include "nl_NL.lang"
};
case inkay_language::Russian: return {
#include "ru_RU.lang"
};
case inkay_language::Korean: return {
#include "ko_KR.lang"
};
case inkay_language::EnUwU: return {
#include "en@uwu.lang"
};
}
}

47
common/lang.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
#include <string_view>
enum inkay_language {
// Wii U System Languages
Japanese = 0,
English,
French,
German,
Italian,
Spanish,
SimplifiedChinese,
Korean,
Dutch,
Portuguese,
Russian,
TraditionalChinese,
Invalid,
// Custom Languages
System,
EnUwU,
};
struct config_strings {
const char *plugin_name;
std::string_view network_category;
std::string_view connect_to_network_setting;
std::string_view show_startup_toast_setting;
std::string_view other_category;
std::string_view reset_wwp_setting;
std::string_view press_a_action;
std::string_view restart_to_apply_action;
std::string_view need_menu_action;
std::string_view using_nintendo_network;
std::string_view using_pretendo_network;
std::string_view multiplayer_port_display;
std::string_view module_not_found;
std::string_view module_init_not_found;
std::string_view setting_yes;
std::string_view setting_no;
std::string_view language_setting;
std::string_view language_name;
std::string_view system_language;
};
config_strings get_config_strings(inkay_language language);

115
common/sysconfig.cpp Normal file
View file

@ -0,0 +1,115 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Copyright 2020-2022 V10lator <v10lator@myway.de>
Copyright 2022 Xpl0itU <DaThinkingChair@protonmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sysconfig.h"
#include "utils/logger.h"
#include "utils/scope_exit.h"
#include <coreinit/mcp.h>
#include <coreinit/userconfig.h>
#include <optional>
nn::swkbd::LanguageType get_system_language() {
static std::optional <nn::swkbd::LanguageType> cached_language{};
if (cached_language) return *cached_language;
UCHandle handle = UCOpen();
if (handle < 0) {
DEBUG_FUNCTION_LINE("Error opening UC: %d", handle);
return nn::swkbd::LanguageType::English;
}
scope_exit uc_c([&] { UCClose(handle); });
nn::swkbd::LanguageType language;
alignas(0x40) UCSysConfig settings = {
.name = "cafe.language",
.access = 0,
.dataType = UC_DATATYPE_UNSIGNED_INT,
.error = UC_ERROR_OK,
.dataSize = sizeof(language),
.data = &language,
};
UCError err = UCReadSysConfig(handle, 1, &settings);
if (err != UC_ERROR_OK) {
DEBUG_FUNCTION_LINE("Error reading UC: %d!", err);
return nn::swkbd::LanguageType::English;
}
DEBUG_FUNCTION_LINE_VERBOSE("System language found: %d", static_cast<int>(language));
cached_language = language;
return language;
}
static std::optional<MCPSysProdSettings> mcp_config;
static std::optional<MCPSystemVersion> mcp_os_version;
static void get_mcp_config() {
int mcp = MCP_Open();
scope_exit mcp_c([&] { MCP_Close(mcp); });
alignas(0x40) MCPSysProdSettings config {};
if (MCP_GetSysProdSettings(mcp, &config)) {
DEBUG_FUNCTION_LINE("Could not get MCP system config!");
return;
}
mcp_config = config;
//get os version
MCPSystemVersion os_version;
if (MCP_GetSystemVersion(mcp, &os_version)) {
DEBUG_FUNCTION_LINE("Could not get MCP system config!");
return;
}
mcp_os_version = os_version;
DEBUG_FUNCTION_LINE_VERBOSE("Running on %d.%d.%d%c; %s%s",
os_version.major, os_version.minor, os_version.patch, os_version.region,
config.code_id, config.serial_id
);
}
const char * get_console_serial() {
if (!mcp_config) get_mcp_config();
return mcp_config ? mcp_config->serial_id : "123456789";
}
MCPSystemVersion get_console_os_version() {
if (!mcp_os_version) get_mcp_config();
return mcp_os_version.value_or((MCPSystemVersion) { .major = 5, .minor = 5, .patch = 5, .region = 'E' });
}
static inline int digit(char a) {
if (a < '0' || a > '9') return 0;
return a - '0';
}
uint16_t get_console_peertopeer_port() {
const char * serial = get_console_serial();
uint16_t port = 50000 +
(digit(serial[4]) * 1000) +
(digit(serial[5]) * 100 ) +
(digit(serial[6]) * 10 ) +
(digit(serial[7]) * 1 );
return port;
}

View file

@ -6,7 +6,11 @@
#define INKAY_SYSCONFIG_H
#include <nn/swkbd.h>
#include <coreinit/mcp.h>
nn::swkbd::LanguageType get_system_language();
const char * get_console_serial();
MCPSystemVersion get_console_os_version();
unsigned short get_console_peertopeer_port();
#endif //INKAY_SYSCONFIG_H

13
common/utils/scope_exit.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include <functional>
// https://stackoverflow.com/a/61242721
template<typename F>
struct scope_exit
{
F func;
explicit scope_exit(F&& f): func(std::forward<F>(f)) {}
~scope_exit() { func(); }
};
template<typename F> scope_exit(F&& frv) -> scope_exit<F>;

149
plugin/Makefile Normal file
View file

@ -0,0 +1,149 @@
#-------------------------------------------------------------------------------
.SUFFIXES:
#-------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/wups/share/wups_rules
WUT_ROOT := $(DEVKITPRO)/wut
WUMS_ROOT := $(DEVKITPRO)/wums
#-------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#-------------------------------------------------------------------------------
TARGET := Malamar-spfn
BUILD := build
SOURCES := src src/utils ../common
DATA := data
INCLUDES := src ../src/lang ../common
#DEBUG := 1
#-------------------------------------------------------------------------------
# options for code generation
#-------------------------------------------------------------------------------
OPT := -Os -fno-exceptions -fno-asynchronous-unwind-tables
CFLAGS := -Wall -ffunction-sections -fdata-sections \
$(MACHDEP) $(OPT)
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
ifeq ($(DEBUG),1)
CFLAGS += -DDEBUG -g
endif
CXXFLAGS := $(CFLAGS) -std=c++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(OPT) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -Wl,-gc-sections -Wl,--discard-all
LDFLAGS += $(WUPSSPECS)
LIBS := -lwups -lwut -lnotifications
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
# containing include and lib
#-------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUPS_ROOT) $(WUT_ROOT)
#-------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#-------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#-------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#-------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#-------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#-------------------------------------------------------------------------------
export LD := $(CC)
#-------------------------------------------------------------------------------
else
#-------------------------------------------------------------------------------
export LD := $(CXX)
#-------------------------------------------------------------------------------
endif
#-------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean all
#-------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#-------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf
#-------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#-------------------------------------------------------------------------------
# main targets
#-------------------------------------------------------------------------------
all : $(OUTPUT).wps
$(OUTPUT).wps : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#-------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#-------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#-------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
%.pem.o %_pem.h : %.pem
#-------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#-------------------------------------------------------------------------------
endif
#-------------------------------------------------------------------------------

331
plugin/src/config.cpp Normal file
View file

@ -0,0 +1,331 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "wut_extra.h"
#include "utils/logger.h"
#include "sysconfig.h"
#include "lang.h"
#include <wups.h>
#include <wups/storage.h>
#include <wups/config_api.h>
#include <wups/config/WUPSConfigItemMultipleValues.h>
#include <wups/config/WUPSConfigItemBoolean.h>
#include <wups/config/WUPSConfigItemStub.h>
#include <coreinit/title.h>
#include <coreinit/launch.h>
#include <sysapp/title.h>
#include <sysapp/launch.h>
#include <nn/act.h>
#include <format>
static config_strings strings;
bool Config::connect_to_network = true;
bool Config::show_startup_toast = true;
bool Config::need_relaunch = false;
bool Config::unregister_task_item_pressed = false;
bool Config::is_wiiu_menu = false;
uint32_t Config::language = 13;
inkay_language Config::current_language = English;
static WUPSConfigAPICallbackStatus report_error(WUPSConfigAPIStatus err, int line) {
DEBUG_FUNCTION_LINE_VERBOSE("WUPS config error: %s (L%04d)", WUPSConfigAPI_GetStatusStr(err), line);
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
}
static void report_storage_error(WUPSStorageError err) {
DEBUG_FUNCTION_LINE_VERBOSE("WUPS storage error: %s", WUPSStorageAPI_GetStatusStr(err));
}
static void connect_to_network_changed(ConfigItemBoolean *item, bool new_value) {
DEBUG_FUNCTION_LINE_VERBOSE("connect_to_network changed to: %d", new_value);
if (new_value != Config::connect_to_network) {
Config::need_relaunch = true;
}
Config::connect_to_network = new_value;
WUPSStorageError res = WUPSStorageAPI_StoreBool(nullptr, "connect_to_network", Config::connect_to_network);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
}
static void language_changed(ConfigItemMultipleValues *item, uint32_t new_value) {
DEBUG_FUNCTION_LINE_VERBOSE("language changed to: %d", new_value);
if (new_value != Config::language) {
if (new_value == inkay_language::System)
Config::current_language = (inkay_language) get_system_language();
else
Config::current_language = (inkay_language) new_value;
}
Config::language = new_value;
WUPSStorageError res = WUPSStorageAPI_StoreU32(nullptr, "language", Config::language);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
}
static void show_startup_toast_changed(ConfigItemBoolean *item, bool new_value) {
DEBUG_FUNCTION_LINE_VERBOSE("show_startup_toast changed to: %d", new_value);
Config::show_startup_toast = new_value;
WUPSStorageError res = WUPSStorageAPI_StoreBool(nullptr, "show_startup_toast", Config::show_startup_toast);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
}
static void unregister_task_item_on_input_cb(void *context, WUPSConfigSimplePadData input) {
if (!Config::unregister_task_item_pressed && Config::is_wiiu_menu && (
(input.buttons_d & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A)) {
nn::act::Initialize();
Initialize__Q2_2nn4bossFv();
for (uint8_t i = 1; i <= nn::act::GetNumOfAccounts(); i++) {
if (nn::act::IsSlotOccupied(i) && nn::act::IsNetworkAccountEx(i)) {
nn::boss::Task task{};
nn::act::PersistentId persistentId = nn::act::GetPersistentIdEx(i);
__ct__Q3_2nn4boss4TaskFv(&task);
Initialize__Q3_2nn4boss4TaskFPCcUi(&task, "oltopic", persistentId);
// bypasses compiler warning about unused variable
#ifdef DEBUG
uint32_t res = Unregister__Q3_2nn4boss4TaskFv(&task);
DEBUG_FUNCTION_LINE_VERBOSE("Unregistered oltopic for: SlotNo %d | Persistent ID %08x -> 0x%08x", i,
persistentId, res);
#else
Unregister__Q3_2nn4boss4TaskFv(&task);
#endif
}
}
Finalize__Q2_2nn4bossFv();
nn::act::Finalize();
Config::unregister_task_item_pressed = !Config::unregister_task_item_pressed;
Config::need_relaunch = true;
}
}
static int32_t unregister_task_item_get_display_value(void *context, char *out_buf, int32_t out_size) {
auto string = strings.need_menu_action;
if (Config::is_wiiu_menu) {
if (Config::unregister_task_item_pressed) {
string = strings.restart_to_apply_action;
} else {
string = strings.press_a_action;
}
}
if ((int) string.length() > out_size - 1) return -1;
string.copy(out_buf, string.length());
out_buf[string.length()] = '\0';
return 0;
}
static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle root_cat) {
WUPSConfigAPIStatus err;
uint64_t current_title_id = OSGetTitleID();
uint64_t wiiu_menu_tid = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_WII_U_MENU);
Config::is_wiiu_menu = (current_title_id == wiiu_menu_tid);
// get translation strings
strings = get_config_strings(Config::current_language);
// Network submenu
// Connect to Pretendo Network Yes/No
// Using UDP port 5000 for multiplayer
WUPSConfigCategoryHandle network_cat;
err = WUPSConfigAPI_Category_Create({strings.network_category.data()}, &network_cat);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
WUPSConfigItemHandle connect_item;
err = WUPSConfigItemBoolean_CreateEx(
"connect_to_network", strings.connect_to_network_setting.data(), true, Config::connect_to_network,
&connect_to_network_changed, strings.setting_yes.data(), strings.setting_no.data(), &connect_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(network_cat, connect_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
const uint16_t port = get_console_peertopeer_port();
char buffer[256];
snprintf(buffer, sizeof(buffer), strings.multiplayer_port_display.data(), port);
WUPSConfigItemHandle multiplayer_port_display;
err = WUPSConfigItemStub_Create(buffer, &multiplayer_port_display);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(network_cat, multiplayer_port_display);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
// Add to root and finish
err = WUPSConfigAPI_Category_AddCategory(root_cat, network_cat);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
// Other category
// Reset Wara Wara Plaza Press A
// Language English/Spanish/etc..
// Show startup toast Yes/No
WUPSConfigCategoryHandle other_cat;
err = WUPSConfigAPI_Category_Create({strings.other_category.data()}, &other_cat);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
WUPSConfigItemHandle unregisterTasksItem;
err = WUPSConfigAPI_Item_Create({
.displayName = strings.reset_wwp_setting.data(),
.context = nullptr,
.callbacks = {
.getCurrentValueDisplay = unregister_task_item_get_display_value,
.getCurrentValueSelectedDisplay = unregister_task_item_get_display_value,
.onSelected = nullptr,
.restoreDefault = nullptr,
.isMovementAllowed = nullptr,
.onCloseCallback = nullptr,
.onInput = unregister_task_item_on_input_cb,
.onInputEx = nullptr,
.onDelete = nullptr
},
}, &unregisterTasksItem);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(other_cat, unregisterTasksItem);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
// TODO localise these!
constexpr std::array<ConfigItemMultipleValuesPair, 14> languages = {
{
{0, "Japanese"},
{1, "English"},
{2, "French"},
{3, "German"},
{4, "Italian"},
{5, "Spanish"},
{6, "Simplified Chinese"},
{7, "Korean"},
{8, "Dutch"},
{9, "Portuguese"},
{10, "Russian"},
{11, "Traditional Chinese"},
{13, "System"},
{14, "English (UwU)"},
}
};
constexpr auto find_lang = [languages](const uint32_t lang) -> int {
return std::ranges::find_if(languages, [lang](auto const l) { return l.value == lang; }) -
std::begin(languages);
};
constexpr int system_language = find_lang(13 /* System */);
const int current_language = find_lang(Config::language);
WUPSConfigItemHandle language_item;
err = WUPSConfigItemMultipleValues_Create("language", strings.language_setting.data(), system_language,
current_language,
const_cast<ConfigItemMultipleValuesPair *>(languages.data()), // Yikes!
languages.size(), &language_changed, &language_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(other_cat, language_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
WUPSConfigItemHandle startup_toast_item;
err = WUPSConfigItemBoolean_CreateEx("show_startup_toast", strings.show_startup_toast_setting.data(), true,
Config::show_startup_toast, &show_startup_toast_changed,
strings.setting_yes.data(), strings.setting_no.data(), &startup_toast_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(other_cat, startup_toast_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
// finish up
err = WUPSConfigAPI_Category_AddCategory(root_cat, other_cat);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
}
static void ConfigMenuClosedCallback() {
// Save all changes
WUPSStorageError res = WUPSStorageAPI_SaveStorage(false);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
if (Config::need_relaunch) {
// Need to reload the console so the patches reset
OSForceFullRelaunch();
SYSLaunchMenu();
Config::need_relaunch = false;
}
}
void Config::Init() {
// Init the config api
WUPSConfigAPIStatus cres =
WUPSConfigAPI_Init({.name = "Inkay"}, ConfigMenuOpenedCallback, ConfigMenuClosedCallback);
if (cres != WUPSCONFIG_API_RESULT_SUCCESS) return (void) report_error(cres, __LINE__);
WUPSStorageError res;
// Try to get values from storage
res = WUPSStorageAPI_GetBool(nullptr, "connect_to_network", &Config::connect_to_network);
if (res == WUPS_STORAGE_ERROR_NOT_FOUND) {
DEBUG_FUNCTION_LINE_VERBOSE("Connect to network value not found, attempting to migrate/create");
bool skipPatches = false;
if (WUPSStorageAPI_GetBool(nullptr, "skipPatches", &skipPatches) == WUPS_STORAGE_ERROR_SUCCESS) {
// Migrate old config value
Config::connect_to_network = !skipPatches;
WUPSStorageAPI_DeleteItem(nullptr, "skipPatches");
}
// Add the value to the storage if it's missing.
res = WUPSStorageAPI_StoreBool(nullptr, "connect_to_network", connect_to_network);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
} else if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
res = WUPSStorageAPI_GetU32(nullptr, "language", &Config::language);
if (res == WUPS_STORAGE_ERROR_NOT_FOUND) {
DEBUG_FUNCTION_LINE_VERBOSE("Language value not found, attempting to create");
// Add the value to the storage.
res = WUPSStorageAPI_StoreU32(nullptr, "language", Config::language);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
} else if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
res = WUPSStorageAPI_GetBool(nullptr, "show_startup_toast", &Config::show_startup_toast);
if (res == WUPS_STORAGE_ERROR_NOT_FOUND) {
DEBUG_FUNCTION_LINE_VERBOSE("Show startup toast value not found, attempting to create");
// Add the value to the storage if it's missing.
res = WUPSStorageAPI_StoreBool(nullptr, "show_startup_toast", show_startup_toast);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
} else if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
// Set the language that's currently used
if (Config::language == inkay_language::System)
Config::current_language = (inkay_language) get_system_language();
else
Config::current_language = (inkay_language) Config::language;
// Save storage
res = WUPSStorageAPI_SaveStorage(false);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
}

31
plugin/src/config.h Normal file
View file

@ -0,0 +1,31 @@
//
// Created by ash on 10/12/22.
//
#ifndef INKAY_CONFIG_H
#define INKAY_CONFIG_H
#include <stdint.h>
#include "lang.h"
class Config {
public:
static void Init();
// wups config items
static bool connect_to_network;
static uint32_t language;
static bool show_startup_toast;
// private stuff
static bool need_relaunch;
// private stuff
static bool is_wiiu_menu;
static inkay_language current_language;
static bool unregister_task_item_pressed;
};
#endif //INKAY_CONFIG_H

152
plugin/src/main.cpp Normal file
View file

@ -0,0 +1,152 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
Copyright 2019 Maschell
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <wups.h>
#include <notifications/notifications.h>
#include <utils/logger.h>
#include "config.h"
#include<coreinit/title.h>
#include<coreinit/dynload.h>
#include<vector>
#include "module.h"
#define INKAY_VERSION "v3.1.0"
/**
Mandatory plugin information.
If not set correctly, the loader will refuse to use the plugin.
**/
WUPS_META(name, "Malamar (SPFN)");
WUPS_META(wups, WUPS_VERSION_STR);
WUPS_USE_WUT_MALLOC();
WUPS_USE_WUT_SOCKETS();
WUPS_USE_WUT_NEWLIB();
WUPS___INIT_WRAPPER();
WUPS___FINI_WRAPPER();
WUPS_INIT_CONFIG_FUNCTIONS();
WUPS_META(buildtimestamp, __DATE__ " " __TIME__);
extern const char wups_meta_plugin_name[] WUPS_SECTION("meta");
const char wups_meta_plugin_name[] = "Malamar (SPFN)";
extern const char wups_meta_info_dump[] WUPS_SECTION("meta");
const char wups_meta_info_dump[] = "(plugin: Malamar (SPFN);" \
"wups " WUPS_VERSION_STR ";" \
"buildtime: " __DATE__ " " __TIME__ ")";
extern const char wups_meta_info_linking_order[] WUPS_SECTION("meta");
const char wups_meta_info_linking_order[] = "Loading \"Malamar (SPFN)\" failed.\n" \
"Function \"wut_get_thread_specific\" returned unexpected value.\n" \
"Please check linking order (expected \"-lwups -lwut\")";
WUPS_PLUGIN_DESCRIPTION("SPFN Network Patcher");
WUPS_PLUGIN_VERSION(INKAY_VERSION);
WUPS_PLUGIN_AUTHOR("redbinder0526, kittentm and Pretendo contributors");
WUPS_PLUGIN_LICENSE("GPLv3");
WUPS_USE_STORAGE("inkay");
WUPS_USE_WUT_DEVOPTAB();
bool is_current_game_splatoon(){
auto current_game = OSGetTitleID();
if(
current_game == 0x0005000010162B00 ||
current_game == 0x0005000010176900 ||
current_game == 0x0005000010176A00
){
return true;
}
return false;
}
void splatoon_patcher(){
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Splatoon patches skipped");
return;
}
if(is_current_game_splatoon()){
NotificationModule_AddInfoNotification("Splatoon Detected, applying patch");
auto rpl_count = OSDynLoad_GetNumberOfRPLs();
std::vector<OSDynLoad_NotifyData> rpls(rpl_count);
if(!OSDynLoad_GetRPLInfo(0, rpl_count, rpls.data())){
NotificationModule_AddInfoNotification("failed to get rpls");
}
for(auto rpl: rpls){
if(rpl.name != 0){
if(strstr(rpl.name, "Gambit") != NULL){
NotificationModule_AddInfoNotification(rpl.name);
auto text_color = NMColor();
text_color.r = 255;
text_color.g = 255;
text_color.b = 255;
text_color.a = 255;
auto bg_color = NMColor();
bg_color.r = 0;
bg_color.g = 0;
bg_color.b = 0;
bg_color.a = 0;
NotificationModule_AddInfoNotificationEx(rpl.name, 30.0f, text_color, bg_color, nullptr, nullptr, true);
NotificationModule_AddInfoNotification("applying patches!!!!");
void* game_data_region = (void*)rpl.dataAddr;
auto str_loc = (char*)(0x000009ac + (uint32_t)game_data_region);
strcpy(str_loc, "https://splatpost.spbr.net/post");
}
}
}
}
}
INITIALIZE_PLUGIN() {
WHBLogCafeInit();
Config::Init();
if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed");
}
// if using pretendo then (try to) apply the ssl patches
Inkay_Initialize(Config::connect_to_network, Config::show_startup_toast);
}
DEINITIALIZE_PLUGIN() {
Inkay_Finalize();
NotificationModule_DeInitLibrary();
WHBLogCafeDeinit();
}
ON_APPLICATION_START() {
// Tell the module the plugin is running!
splatoon_patcher();
Inkay_SetPluginRunning();
}
ON_APPLICATION_ENDS() {
}

93
plugin/src/module.cpp Normal file
View file

@ -0,0 +1,93 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "Notification.h"
#include "utils/logger.h"
#include "config.h"
#include "lang.h"
#include <coreinit/dynload.h>
static OSDynLoad_Module module;
static void (*moduleInitialize)(bool, bool, inkay_language) = nullptr;
static InkayStatus (*moduleGetStatus)() = nullptr;
static void (*moduleSetPluginRunning)() = nullptr;
static const char *get_module_not_found_message() {
return get_config_strings(Config::current_language).module_not_found.data();
}
static const char *get_module_init_not_found_message() {
return get_config_strings(Config::current_language).module_init_not_found.data();
}
void Inkay_Initialize(bool apply_patches, bool show_startup_toast) {
if (module) {
return;
}
if (OSDynLoad_Acquire("inkay", &module) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to acquire module");
ShowNotification(get_module_not_found_message());
return;
}
if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "Inkay_Initialize", reinterpret_cast<void * *>(&moduleInitialize)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to find function %s", "Inkay_Initialize");
ShowNotification(get_module_init_not_found_message());
OSDynLoad_Release(module);
return;
}
moduleInitialize(apply_patches, show_startup_toast, Config::current_language);
}
void Inkay_Finalize() {
if (module) {
OSDynLoad_Release(module);
moduleInitialize = nullptr;
moduleGetStatus = nullptr;
moduleSetPluginRunning = nullptr;
}
}
InkayStatus Inkay_GetStatus() {
if (!module) {
return InkayStatus::Error;
}
if (!moduleGetStatus && OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "Inkay_GetStatus", reinterpret_cast<void * *>(&moduleGetStatus)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to find function %s", "Inkay_GetStatus");
return InkayStatus::Error;
}
return moduleGetStatus();
}
void Inkay_SetPluginRunning() {
if (!module) {
return;
}
if (!moduleSetPluginRunning && OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "Inkay_SetPluginRunning", reinterpret_cast<void * *>(&moduleSetPluginRunning)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to find function %s", "Inkay_SetPluginRunning");
return;
}
moduleSetPluginRunning();
}

30
plugin/src/module.h Normal file
View file

@ -0,0 +1,30 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
enum class InkayStatus {
Uninitialized, ///< The module isn't initialized
Nintendo, ///< The module is initialized but hasn't applied any patches
Pretendo, ///< The module is initialized and has applied the Pretendo patches
Error = -1 ///< Failed to retrieve the module status
};
void Inkay_Initialize(bool apply_patches, bool show_startup_toast);
void Inkay_Finalize();
InkayStatus Inkay_GetStatus();
void Inkay_SetPluginRunning();

36
plugin/src/utils/logger.h Normal file
View file

@ -0,0 +1,36 @@
#pragma once
#include <string.h>
#include <whb/log.h>
#include <whb/log_module.h>
#include <whb/log_cafe.h>
#include <whb/log_udp.h>
#ifdef __cplusplus
extern "C" {
#endif
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \
OSFatal_printf("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \
WHBLogPrintf("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0);
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \
WHBLogWritef("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0);
#ifdef DEBUG
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) DEBUG_FUNCTION_LINE(FMT, ##ARGS)
#else
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0);
#endif
#ifdef __cplusplus
}
#endif

3
renovate.json Normal file
View file

@ -0,0 +1,3 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

View file

@ -1,5 +0,0 @@
#pragma once
#include <string_view>
void ShowNotification(std::string_view notification);

View file

@ -17,244 +17,9 @@
#include "config.h"
#include "wut_extra.h"
#include "utils/logger.h"
#include "utils/sysconfig.h"
#include <wups.h>
#include <wups/storage.h>
#include <wups/config_api.h>
#include <wups/config/WUPSConfigItemBoolean.h>
#include <coreinit/title.h>
#include <coreinit/launch.h>
#include <sysapp/title.h>
#include <sysapp/launch.h>
#include <nn/act.h>
bool Config::connect_to_network = true;
bool Config::need_relaunch = false;
bool Config::unregister_task_item_pressed = false;
bool Config::is_wiiu_menu = false;
config_strings strings;
constexpr config_strings get_config_strings(nn::swkbd::LanguageType language) {
switch (language) {
case nn::swkbd::LanguageType::English:
default:
return {
.plugin_name = "Inkay",
.network_category = "Network selection",
.connect_to_network_setting = "Connect to the Pretendo network",
.other_category = "Other settings",
.reset_wwp_setting = "Reset Wara Wara Plaza",
.press_a_action = "Press A",
.restart_to_apply_action = "Restart to apply",
.need_menu_action = "From WiiU menu only"
};
case nn::swkbd::LanguageType::Spanish:
return {
.plugin_name = "Inkay",
.network_category = "Selección de red",
.connect_to_network_setting = "Conectar a la red Pretendo",
.other_category = "Otros ajustes",
.reset_wwp_setting = "Restablecer Wara Wara Plaza",
.press_a_action = "Pulsa A",
.restart_to_apply_action = "Reinicia para confirmar",
.need_menu_action = "Sólo desde el menú de WiiU",
};
case nn::swkbd::LanguageType::French:
return {
.plugin_name = "Inkay",
.network_category = "Sélection du réseau",
.connect_to_network_setting = "Connexion à Pretendo",
.other_category = "Autres paramètres",
.reset_wwp_setting = "Réinitialiser la place WaraWara",
.press_a_action = "Appuyez sur A",
.restart_to_apply_action = "Redémarrer pour appliquer",
.need_menu_action = "Depuis le menu Wii U seulement",
};
case nn::swkbd::LanguageType::Italian:
return {
.plugin_name = "Inkay",
.network_category = "Selezione della rete",
.connect_to_network_setting = "Connettiti alla rete Pretendo",
.other_category = "Altre categorie",
.reset_wwp_setting = "Ripristina Wara Wara Plaza",
.press_a_action = "Premi A",
.restart_to_apply_action = "Riavvia per applicare",
.need_menu_action = "Solo dal menu WiiU",
};
case nn::swkbd::LanguageType::German:
return {
.plugin_name = "Inkay",
.network_category = "Netzwerkauswahl",
.connect_to_network_setting = "Verbinde zum Pretendo Network",
.other_category = "Andere Einstellungen",
.reset_wwp_setting = "Wara Wara Plaza zurücksetzen",
.press_a_action = "Drücke A",
.restart_to_apply_action = "Neustarten zum Anwenden",
.need_menu_action = "Nur vom Wii U-Menü aus",
};
}
}
static void connect_to_network_changed(ConfigItemBoolean* item, bool new_value) {
DEBUG_FUNCTION_LINE("connect_to_network changed to: %d", new_value);
if (new_value != Config::connect_to_network) {
Config::need_relaunch = true;
}
Config::connect_to_network = new_value;
if (WUPSStorageAPI::Store<bool>("connect_to_network", Config::connect_to_network) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to save \"connect_to_network\" value (%d)", Config::connect_to_network);
}
}
static void unregister_task_item_on_input_cb(void *context, WUPSConfigSimplePadData input) {
if (!Config::unregister_task_item_pressed && Config::is_wiiu_menu && ((input.buttons_d & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A)) {
nn::act::Initialize();
Initialize__Q2_2nn4bossFv();
for (uint8_t i = 1; i <= nn::act::GetNumOfAccounts(); i++)
{
if (nn::act::IsSlotOccupied(i) && nn::act::IsNetworkAccountEx(i))
{
nn::boss::Task task{};
nn::act::PersistentId persistentId = nn::act::GetPersistentIdEx(i);
__ct__Q3_2nn4boss4TaskFv(&task);
Initialize__Q3_2nn4boss4TaskFPCcUi(&task, "oltopic", persistentId);
// bypasses compiler warning about unused variable
#ifdef DEBUG
uint32_t res = Unregister__Q3_2nn4boss4TaskFv(&task);
DEBUG_FUNCTION_LINE_VERBOSE("Unregistered oltopic for: SlotNo %d | Persistent ID %08x -> 0x%08x", i, persistentId, res);
#else
Unregister__Q3_2nn4boss4TaskFv(&task);
#endif
}
}
Finalize__Q2_2nn4bossFv();
nn::act::Finalize();
Config::unregister_task_item_pressed = !Config::unregister_task_item_pressed;
Config::need_relaunch = true;
}
}
static int32_t unregister_task_item_get_display_value(void *context, char *out_buf, int32_t out_size) {
if (!Config::is_wiiu_menu)
strncpy(out_buf, strings.need_menu_action, out_size);
else
strncpy(out_buf, Config::unregister_task_item_pressed ? strings.restart_to_apply_action : strings.press_a_action, out_size);
return 0;
}
static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) {
uint64_t current_title_id = OSGetTitleID();
uint64_t wiiu_menu_tid = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_WII_U_MENU);
Config::is_wiiu_menu = (current_title_id == wiiu_menu_tid);
// get translation strings
strings = get_config_strings(get_system_language());
// create root config category
WUPSConfigCategory root = WUPSConfigCategory(rootHandle);
auto patching_cat = WUPSConfigCategory::Create(strings.network_category);
// config id display name default current value changed callback
patching_cat.add(WUPSConfigItemBoolean::Create("connect_to_network", strings.connect_to_network_setting, true, Config::connect_to_network, &connect_to_network_changed));
root.add(std::move(patching_cat));
auto other_cat = WUPSConfigCategory::Create(strings.other_category);
WUPSConfigAPIItemCallbacksV2 unregisterTasksItemCallbacks = {
.getCurrentValueDisplay = unregister_task_item_get_display_value,
.getCurrentValueSelectedDisplay = unregister_task_item_get_display_value,
.onSelected = nullptr,
.restoreDefault = nullptr,
.isMovementAllowed = nullptr,
.onCloseCallback = nullptr,
.onInput = unregister_task_item_on_input_cb,
.onInputEx = nullptr,
.onDelete = nullptr
};
WUPSConfigAPIItemOptionsV2 unregisterTasksItemOptions = {
.displayName = strings.reset_wwp_setting,
.context = nullptr,
.callbacks = unregisterTasksItemCallbacks,
};
WUPSConfigItemHandle unregisterTasksItem;
WUPSConfigAPIStatus err;
if ((err = WUPSConfigAPI_Item_Create(unregisterTasksItemOptions, &unregisterTasksItem)) != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Creating config menu failed: %s", WUPSConfigAPI_GetStatusStr(err));
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
}
if ((err = WUPSConfigAPI_Category_AddItem(other_cat.getHandle(), unregisterTasksItem)) != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Creating config menu failed: %s", WUPSConfigAPI_GetStatusStr(err));
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
}
root.add(std::move(other_cat));
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
}
static void ConfigMenuClosedCallback() {
// Save all changes
if (WUPSStorageAPI::SaveStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to save storage");
}
if (Config::need_relaunch) {
// Need to reload the console so the patches reset
OSForceFullRelaunch();
SYSLaunchMenu();
Config::need_relaunch = false;
}
}
void Config::Init() {
// Init the config api
WUPSConfigAPIOptionsV1 configOptions = { .name = "Inkay" };
if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to initialize WUPS Config API");
return;
}
WUPSStorageError storageRes;
// Try to get value from storage
if ((storageRes = WUPSStorageAPI::Get<bool>("connect_to_network", Config::connect_to_network)) == WUPS_STORAGE_ERROR_NOT_FOUND) {
DEBUG_FUNCTION_LINE("Connect to network value not found, attempting to migrate/create");
bool skipPatches = false;
if (WUPSStorageAPI::Get<bool>("skipPatches", skipPatches) == WUPS_STORAGE_ERROR_SUCCESS) {
// Migrate old config value
Config::connect_to_network = !skipPatches;
WUPSStorageAPI::DeleteItem("skipPatches");
}
// Add the value to the storage if it's missing.
if (WUPSStorageAPI::Store<bool>("connect_to_network", connect_to_network) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to store bool");
}
}
else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to get bool %s (%d)", WUPSStorageAPI_GetStatusStr(storageRes), storageRes);
}
// Save storage
if (WUPSStorageAPI::SaveStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to save storage");
}
}
bool Config::connect_to_network = false;
bool Config::show_startup_toast = false;
bool Config::initialized = false;
bool Config::shown_warning = false;
bool Config::plugin_is_loaded = false;
bool Config::block_initialize = false;

View file

@ -7,29 +7,18 @@
class Config {
public:
static void Init();
// wups config items
static bool connect_to_network;
// private stuff
static bool need_relaunch;
static bool show_startup_toast;
// private stuff
static bool is_wiiu_menu;
static bool initialized;
static bool unregister_task_item_pressed;
};
static bool shown_warning;
struct config_strings {
const char *plugin_name;
const char *network_category;
const char *connect_to_network_setting;
const char *other_category;
const char *reset_wwp_setting;
const char *press_a_action;
const char *restart_to_apply_action;
const char *need_menu_action;
static bool plugin_is_loaded;
static bool block_initialize;
};
#endif //INKAY_CONFIG_H

25
src/export.h Normal file
View file

@ -0,0 +1,25 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
enum class InkayStatus {
Uninitialized, ///< The module isn't initialized
Nintendo, ///< The module is initialized but hasn't applied any patches
Pretendo, ///< The module is initialized and has applied the Pretendo patches
Error = -1 ///< Failed to retrieve the module status
};

View file

@ -8,35 +8,35 @@ typedef struct URL_Patch {
static const URL_Patch url_patches[] = {
//nim-boss .rodata
{0xE2282550, "http://pushmore.wup.shop.pretendo.cc/pushmore/r/%s"},
{0xE229A0A0, "http://npns-dev.c.app.pretendo.cc/bst.dat"},
{0xE229A0D0, "http://npns-dev.c.app.pretendo.cc/bst2.dat"},
{0xE2281964, "https://tagaya.wup.shop.pretendo.cc/tagaya/versionlist/%s/%s/%s"},
{0xE22819B4, "https://tagaya.wup.shop.pretendo.cc/tagaya/versionlist/%s/%s/latest_version"},
{0xE2282584, "http://pushmo.wup.shop.pretendo.cc/pushmo/d/%s/%u"},
{0xE22825B8, "http://pushmo.wup.shop.pretendo.cc/pushmo/c/%u/%u"},
{0xE2282DB4, "https://ecs.wup.shop.pretendo.cc/ecs/services/ECommerceSOAP"},
{0xE22830A0, "https://ecs.wup.shop.pretendo.cc/ecs/services/ECommerceSOAP"},
{0xE22830E0, "https://nus.wup.shop.pretendo.cc/nus/services/NetUpdateSOAP"},
{0xE2299990, "nppl.app.pretendo.cc"},
{0xE229A600, "https://pls.wup.shop.pretendo.cc/pls/upload"},
{0xE229A6AC, "https://npvk-dev.app.pretendo.cc/reports"},
{0xE229A6D8, "https://npvk.app.pretendo.cc/reports"},
{0xE229B1F4, "https://npts.app.pretendo.cc/p01/tasksheet/%s/%s/%s/%s?c=%s&l=%s"},
{0xE229B238, "https://npts.app.pretendo.cc/p01/tasksheet/%s/%s/%s?c=%s&l=%s"},
{0xE22AB2D8, "https://idbe-wup.cdn.pretendo.cc/icondata/%02X/%016llX.idbe"},
{0xE22AB318, "https://idbe-ctr.cdn.pretendo.cc/icondata/%02X/%016llX.idbe"},
{0xE22AB358, "https://idbe-wup.cdn.pretendo.cc/icondata/%02X/%016llX-%d.idbe"},
{0xE22AB398, "https://idbe-ctr.cdn.pretendo.cc/icondata/%02X/%016llX-%d.idbe"},
{0xE22B3EF8, "https://ecs.c.shop.pretendo.cc"},
{0xE22B3F30, "https://ecs.c.shop.pretendo.cc/ecs/services/ECommerceSOAP"},
{0xE22B3F70, "https://ias.c.shop.pretendo.cc/ias/services/IdentityAuthenticationSOAP"},
{0xE22B3FBC, "https://cas.c.shop.pretendo.cc/cas/services/CatalogingSOAP"},
{0xE22B3FFC, "https://nus.c.shop.pretendo.cc/nus/services/NetUpdateSOAP"},
{0xE229DE0C, "n.app.pretendo.cc"},
{0xE2282550, "http://pushmorewupshop.spbr.net/pushmore/r/%s"},
{0xE229A0A0, "http://npns-devcapp.spbr.net/bst.dat"},
{0xE229A0D0, "http://npns-devcapp.spbr.net/bst2.dat"},
{0xE2281964, "https://tagayawupshop.spbr.net/tagaya/versionlist/%s/%s/%s"},
{0xE22819B4, "https://tagayawupshop.spbr.net/tagaya/versionlist/%s/%s/latest_version"},
{0xE2282584, "http://pushmowupshop.spbr.net/pushmo/d/%s/%u"},
{0xE22825B8, "http://pushmowupshop.spbr.net/pushmo/c/%u/%u"},
{0xE2282DB4, "https://ecswupshop.spbr.net/ecs/services/ECommerceSOAP"},
{0xE22830A0, "https://ecswupshop.spbr.net/ecs/services/ECommerceSOAP"},
{0xE22830E0, "https://nuswupshop.spbr.net/nus/services/NetUpdateSOAP"},
{0xE2299990, "policylist.spbr.net"},
{0xE229A600, "https://plswupshop.spbr.net/pls/upload"},
{0xE229A6AC, "https://npvk-devapp.spbr.net/reports"},
{0xE229A6D8, "https://npvkapp.spbr.net/reports"},
{0xE229B1F4, "https://tasksheets.spbr.net/p01/tasksheet/%s/%s/%s/%s?c=%s&l=%s"},
{0xE229B238, "https://tasksheets.spbr.net/p01/tasksheet/%s/%s/%s?c=%s&l=%s"},
{0xE22AB2D8, "https://idbe-wupcdn.spbr.net/icondata/%02X/%016llX.idbe"},
{0xE22AB318, "https://idbe-ctrcdn.spbr.net/icondata/%02X/%016llX.idbe"},
{0xE22AB358, "https://idbe-wupcdn.spbr.net/icondata/%02X/%016llX-%d.idbe"},
{0xE22AB398, "https://idbe-ctrcdn.spbr.net/icondata/%02X/%016llX-%d.idbe"},
{0xE22B3EF8, "https://ecscshop.spbr.net"},
{0xE22B3F30, "https://ecscshop.spbr.net/ecs/services/ECommerceSOAP"},
{0xE22B3F70, "https://iascshop.spbr.net/ias/services/IdentityAuthenticationSOAP"},
{0xE22B3FBC, "https://cascshop.spbr.net/cas/services/CatalogingSOAP"},
{0xE22B3FFC, "https://nuscshop.spbr.net/nus/services/NetUpdateSOAP"},
{0xE229DE0C, "n.app.spbr.net"},
//nim-boss .bss
{0xE24B8A24, "https://nppl.app.pretendo.cc/p01/policylist/1/1/UNK"},
{0xE31930D4, "https://%s%saccount.pretendo.cc/v%u/api/"}
{0xE24B8A24, "https://policylist.spbr.net/p01/policylist/1/1/UNK"},
{0xE31930D4, "https://%s%saccount.spbr.net/v%u/api/"}
};

18
src/lang/de_DE.lang Normal file
View file

@ -0,0 +1,18 @@
.plugin_name="Inkay"
,.network_category="Netzwerkauswahl"
,.connect_to_network_setting="Verbinde zum SPFN"
,.other_category="Andere Einstellungen"
,.reset_wwp_setting="Wara Wara Plaza zurücksetzen"
,.press_a_action="Drücke A"
,.restart_to_apply_action="Neustarten zum Anwenden"
,.need_menu_action="Nur vom Wii U-Menü aus"
,.using_nintendo_network="Nutze Nintendo Network"
,.using_pretendo_network="Nutze SPFN"
,.multiplayer_port_display="Nutze UDP Port %hu für Mehrspieler"
,.module_not_found="SPFN Patch fehlgeschlagen - nutze Aroma Updater zum Reparieren (686-1001 Module missing)"
,.module_init_not_found="SPFN Patch fehlgeschlagen - nutze Aroma Updater zum Reparieren (686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Deutsch"
,.system_language=""

19
src/lang/en@uwu.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Inky ^w^"
,.network_category="Newowk sewectiown! :3"
,.connect_to_network_setting="use dah pretender!!!!"
,.show_startup_toast_setting="show da deets :3"
,.other_category="oth3r stuffzz"
,.reset_wwp_setting="warah wara go BYEBYE x3c"
,.press_a_action="Press A plz?"
,.restart_to_apply_action="ima restart ur consoel >:3c"
,.need_menu_action="no menu?? only menu -w-"
,.using_nintendo_network="meanie network!!!"
,.using_pretendo_network="pretender netw3rk >:D"
,.multiplayer_port_display="mah secret word is %hu... ur modem will know"
,.module_not_found="oh noez! pls get aroma updater help :( (686-1001 Module missing)"
,.module_init_not_found="oh noez! pls get aroma updater help :( (686-1002 Module init)"
,.setting_yes="yaah!"
,.setting_no="nu >w<"
,.language_setting="spek..."
,.language_name="kitteh speak!!"
,.system_language="System"

19
src/lang/en_US.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Malamar"
,.network_category="Network selection"
,.connect_to_network_setting="Connect to SPFN"
,.show_startup_toast_setting="Show startup toast"
,.other_category="Other settings"
,.reset_wwp_setting="Reset Wara Wara Plaza"
,.press_a_action="Press A"
,.restart_to_apply_action="Restart to apply"
,.need_menu_action="From WiiU menu only"
,.using_nintendo_network="Using Nintendo Network"
,.using_pretendo_network="Using SPFN"
,.multiplayer_port_display="Using UDP port %hu for multiplayer"
,.module_not_found="SPFN patch failed - use Aroma Updater to repair (686-1001 Module missing)"
,.module_init_not_found="SPFN patch failed - use Aroma Updater to repair (686-1002 Module init)"
,.setting_yes="Yes"
,.setting_no="No"
,.language_setting="Language"
,.language_name="English"
,.system_language="System"

19
src/lang/es_ES.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Inkay"
,.network_category="Seleccionar red"
,.connect_to_network_setting="Conectar a SPFN"
,.show_startup_toast_setting=""
,.other_category="Otros ajustes"
,.reset_wwp_setting="Restablecer Plaza Wara Wara"
,.press_a_action="Pulsa A"
,.restart_to_apply_action="Reiniciar para aplicar"
,.need_menu_action="Solo desde el Menú de Wii U"
,.using_nintendo_network="Usando Nintendo Network"
,.using_pretendo_network="Usando SPFN"
,.multiplayer_port_display="Usando puerto UDP %hu para multijugador"
,.module_not_found="Ha fallado el parche SPFN - usa Aroma Updater para repararlo (686-1001 Falta un módulo)"
,.module_init_not_found="Ha fallado el parche SPFN - usa Aroma Updater para repararlo (686-1002 Inicialización del módulo)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Español"
,.system_language=""

18
src/lang/fr_FR.lang Normal file
View file

@ -0,0 +1,18 @@
.plugin_name="Inkay"
,.network_category="Sélection du réseau"
,.connect_to_network_setting="Connexion au SPFN"
,.other_category="Autres paramètres"
,.reset_wwp_setting="Réinitialiser la place WaraWara"
,.press_a_action="Appuyez sur A"
,.restart_to_apply_action="Redémarrer pour appliquer"
,.need_menu_action="(retournez d'abord au menu Wii U)"
,.using_nintendo_network="Réseau sélectionné : Nintendo Network"
,.using_pretendo_network="Réseau sélectionné : SPFN"
,.multiplayer_port_display="Le port UDP %hu sera utilisé pour le multijoueur"
,.module_not_found="Le patch SPFN a rencontré un problème, veuillez le réparer avec Aroma Updater (686-1001 Module missing)"
,.module_init_not_found="Le patch SPFN a rencontré un problème, veuillez le réparer avec Aroma Updater (686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Français"
,.system_language=""

19
src/lang/it_IT.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Inkay"
,.network_category="Seleziona la rete"
,.connect_to_network_setting="Connettiti a SPFN"
,.show_startup_toast_setting=""
,.other_category="Altre impostazioni"
,.reset_wwp_setting="Ripristina Wara Wara Plaza"
,.press_a_action="Premi A"
,.restart_to_apply_action="Riavvia per applicare"
,.need_menu_action="Solo dal menu WiiU"
,.using_nintendo_network="In uso Nintendo Network"
,.using_pretendo_network="In uso SPFN"
,.multiplayer_port_display="È in uso la porta UDP %hu per il multigiocatore"
,.module_not_found="Patch di SPFN fallita - usa Aroma Updater per correggere (686-1001 Modulo mancante)"
,.module_init_not_found="Patch di SPFN fallita - usa Aroma Updater per correggere (686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Italiano"
,.system_language=""

19
src/lang/ja_JP.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Inkay"
,.network_category="ネットワークの選択"
,.connect_to_network_setting="SPFNネットワークに接続する"
,.show_startup_toast_setting=""
,.other_category="その他の設定"
,.reset_wwp_setting="わらわら広場をリセット"
,.press_a_action="Aボタンを押そう"
,.restart_to_apply_action="再起動後に反映されます"
,.need_menu_action="Wii Uメニューからのみ実行可能"
,.using_nintendo_network="選択済み:ニンテンドーネットワーク"
,.using_pretendo_network="選択済みSPFN ネットワーク"
,.multiplayer_port_display="マルチプレイに UDPポート %hu を使用します"
,.module_not_found="SPFN パッチに失敗しました。Aroma Updaterを使用して解決してみてください。(686-1001 Module missing)"
,.module_init_not_found="SPFN パッチに失敗しました。Aroma Updaterを使用して解決してみてください。(686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="日本語"
,.system_language=""

19
src/lang/ko_KR.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Inkay"
,.network_category="네트워크 선택"
,.connect_to_network_setting="SPFN 사용"
,.show_startup_toast_setting=""
,.other_category="기타 설정"
,.reset_wwp_setting="Wara Wara Plaza 초기화"
,.press_a_action="A 버튼을 누르세요"
,.restart_to_apply_action="재시작 후 적용"
,.need_menu_action="Wii U 메뉴에서만"
,.using_nintendo_network="Nintendo Network 사용 중"
,.using_pretendo_network="SPFN 사용 중"
,.multiplayer_port_display="멀티플레이어에 UDP 포트 %hu을(를) 사용합니다"
,.module_not_found="패치를 적용하는 데 실패했습니다. Aroma Updater로 복구해 주세요. (686-1001 모듈 없음)"
,.module_init_not_found="패치를 적용하는 데 실패했습니다. Aroma Updater로 복구해 주세요. (686-1002 모듈 초기화)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="한국어"
,.system_language=""

19
src/lang/nl_NL.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Inkay"
,.network_category="Netwerkselectie"
,.connect_to_network_setting="Verbind met het SPFN-netwerk"
,.show_startup_toast_setting=""
,.other_category="Overige instellingen"
,.reset_wwp_setting="Reset het Wara Wara Plaza"
,.press_a_action="Druk A"
,.restart_to_apply_action="Herstart om toe te passen"
,.need_menu_action="Alleen vanuit het WiiU-menu"
,.using_nintendo_network="Nintendo Network wordt gebruikt"
,.using_pretendo_network="SPFN wordt gebruikt"
,.multiplayer_port_display="Gebruikt UDP port %hu voor multiplayer"
,.module_not_found="SPFN patch mislukt - gebruik Aroma Updater om het te herstellen (686-1001 Module ontbreekt)"
,.module_init_not_found="SPFN patch mislukt - gebruik Aroma Updater om het te herstellen (686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Nederlands"
,.system_language=""

19
src/lang/pt_BR.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Inkay"
,.network_category="Selecionar rede"
,.connect_to_network_setting="Conecta-se à SPFN"
,.show_startup_toast_setting=""
,.other_category="Outras configurações"
,.reset_wwp_setting="Resetar Wara Wara Plaza"
,.press_a_action="Aperte A"
,.restart_to_apply_action="Reinicie para aplicar"
,.need_menu_action="Apenas no menu do Wii U"
,.using_nintendo_network="Usando Nintendo Network"
,.using_pretendo_network="Usando SPFN"
,.multiplayer_port_display="Usando UDP port %hu para a jogatina multiplayer"
,.module_not_found="Atualização do SPFN falhou - use o Aroma Updater para corrigir (686-1001 Módulo faltando)"
,.module_init_not_found="Atualização do SPFN falhou - use o Aroma Updater para corrigir (686-1002 Módulo de início)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Português"
,.system_language=""

19
src/lang/ru_RU.lang Normal file
View file

@ -0,0 +1,19 @@
.plugin_name="Inkay"
,.network_category="Выбор сети"
,.connect_to_network_setting="Подключиться к SPFN"
,.show_startup_toast_setting=""
,.other_category="Другие настройки"
,.reset_wwp_setting="Сбросить Wara Wara Plaza"
,.press_a_action="Нажмите A"
,.restart_to_apply_action="Перезагрузите для применения изменений"
,.need_menu_action="Только из меню Wii U"
,.using_nintendo_network="Используется Nintendo Network"
,.using_pretendo_network="Используется SPFN"
,.multiplayer_port_display="Используем UDP-порт %hu для сетевой игры"
,.module_not_found="Ошибка установки патча SPFN - для восстановления, запустите Aroma Updater (686-1001 Модуль отсутствует)"
,.module_init_not_found="Ошибка установки патча SPFN - для восстановления, запустите Aroma Updater (686-1002 Инициализация модуля)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Русский язык"
,.system_language=""

18
src/lang/zh_CN.lang Normal file
View file

@ -0,0 +1,18 @@
.plugin_name="Inkay"
,.network_category="选择网络"
,.connect_to_network_setting="连接到SPFN"
,.other_category="其他设置"
,.reset_wwp_setting="重置Wara Wara Plaza"
,.press_a_action="请按 A"
,.restart_to_apply_action="重启以应用设置"
,.need_menu_action="仅来自WiiU Menu"
,.using_nintendo_network="使用 Nintendo Network"
,.using_pretendo_network="使用 SPFN"
,.multiplayer_port_display="多人游戏使用 UDP 端口 %hu"
,.module_not_found="SPFN 补丁失败 - 使用 Aroma Updater 修复686-1001 模块丢失)"
,.module_init_not_found="SPFN 补丁失败 - 使用 Aroma Updater 修复686-1002 模块初始化)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="中文(简体)"
,.system_language=""

20
src/lang/zh_Hant.lang Normal file
View file

@ -0,0 +1,20 @@
.plugin_name="Inkay"
,.network_category="選擇網路"
,.connect_to_network_setting="連接到SPFN"
,.show_startup_toast_setting=""
,.other_category="其他設定"
,.reset_wwp_setting="重置Wara Wara Plaza"
,.press_a_action="請按 A"
,.restart_to_apply_action="重啓以套用設定"
,.need_menu_action="僅來自WiiU Menu"
,.using_nintendo_network="使用 Nintendo Network"
,.using_pretendo_network="使用 SPFN"
,.multiplayer_port_display="Using UDP port %hu for multiplayer"
,.module_not_found="SPFN patch failed - use Aroma Updater to repair (686-1001 Module missing)"
,.module_init_not_found="SPFN patch failed - use Aroma Updater to repair (686-1002 Module init)"
,.multiplayer_port_display="使用 UDP 端口 %hu 進行多人遊戲"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="中文(繁體)"
,.system_language=""

View file

@ -16,60 +16,74 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <wups.h>
#include <optional>
#include <nsysnet/nssl.h>
#include <sysapp/title.h>
#include <coreinit/cache.h>
#include <coreinit/dynload.h>
#include <coreinit/mcp.h>
#include <coreinit/memory.h>
#include <coreinit/memorymap.h>
#include <coreinit/memexpheap.h>
#include <coreinit/title.h>
#include <notifications/notifications.h>
#include <utils/logger.h>
#include "export.h"
#include "iosu_url_patches.h"
#include "config.h"
#include "Notification.h"
#include "patches/olv_urls.h"
#include "patches/game_matchmaking.h"
#include <coreinit/filesystem.h>
#include <cstring>
#include <string>
#include <nn/erreula/erreula_cpp.h>
#include <nn/act/client_cpp.h>
#include <wums.h>
#include <coreinit/dynload.h>
#include <coreinit/mcp.h>
#include <notifications/notifications.h>
#include <utils/logger.h>
#include <string>
#include <optional>
#include <cstring>
#include <cstdint>
#include <curl/curl.h>
#include "ca_pem.h"
#include <gx2/surface.h>
#define INKAY_VERSION "v2.6.0"
#define INKAY_VERSION "v3.1.0"
/**
Mandatory plugin information.
If not set correctly, the loader will refuse to use the plugin.
**/
WUPS_PLUGIN_NAME("Inkay");
WUPS_PLUGIN_DESCRIPTION("Pretendo Network Patcher");
WUPS_PLUGIN_VERSION(INKAY_VERSION);
WUPS_PLUGIN_AUTHOR("Pretendo contributors");
WUPS_PLUGIN_LICENSE("GPLv3");
WUMS_META(export_name, "inkay");
WUMS_META(wums, WUMS_VERSION);
WUMS_USE_WUT_MALLOC();
WUMS_USE_WUT_SOCKETS();
WUMS_USE_WUT_NEWLIB();
WUMS___INIT_WRAPPER();
WUMS___FINI_WRAPPER();
WUMS_META(buildtimestamp, __DATE__ " " __TIME__);
extern const char wums_meta_module_name[] WUMS_SECTION("meta");
const char wums_meta_module_name[] = "inkay";
extern const char wums_meta_info_dump[] WUMS_SECTION("meta");
const char wums_meta_info_dump[] = "(module: inkay;" \
"wums " WUMS_VERSION ";" \
"buildtime: " __DATE__ " " __TIME__ ")";
extern const char wums_meta_info_linking_order[] WUMS_SECTION("meta");
const char wums_meta_info_linking_order[] = "Loading module \"inkay\" failed.\n" \
"Function \"wut_get_thread_specific\" returned unexpected value.\n" \
"Please check linking order (expected \"-lwums -lwut\")";
WUPS_USE_STORAGE("inkay");
WUMS_MODULE_DESCRIPTION("SPFN Network Patcher");
WUMS_MODULE_VERSION(INKAY_VERSION);
WUMS_MODULE_AUTHOR("Pretendo contributors and RusticMaple");
WUMS_MODULE_LICENSE("GPLv3");
WUPS_USE_WUT_DEVOPTAB();
WUMS_DEPENDS_ON(homebrew_functionpatcher);
WUMS_DEPENDS_ON(homebrew_kernel);
WUMS_DEPENDS_ON(homebrew_notifications);
WUMS_USE_WUT_DEVOPTAB();
#include <kernel/kernel.h>
#include <mocha/mocha.h>
#include <function_patcher/function_patching.h>
#include "patches/account_settings.h"
#include "utils/sysconfig.h"
#include "patches/dns_hooks.h"
#include "patches/eshop_applet.h"
#include "patches/olv_applet.h"
#include "patches/game_peertopeer.h"
#include "sysconfig.h"
#include "lang.h"
//thanks @Gary#4139 :p
static void write_string(uint32_t addr, const char *str) {
@ -97,78 +111,51 @@ static bool is555(MCPSystemVersion version) {
return (version.major == 5) && (version.minor == 5) && (version.patch >= 5);
}
static const char *get_nintendo_network_message() {
static const char *get_nintendo_network_message(inkay_language language) {
// TL note: "Nintendo Network" is a proper noun - "Network" is part of the name
// TL note: "Using" instead of "Connected" is deliberate - we don't know if a successful connection exists, we are
// only specifying what we'll *attempt* to connect to
switch (get_system_language()) {
case nn::swkbd::LanguageType::English:
default:
return "Using Nintendo Network";
case nn::swkbd::LanguageType::Spanish:
return "Usando Nintendo Network";
case nn::swkbd::LanguageType::French:
return "Sur Nintendo Network";
case nn::swkbd::LanguageType::Italian:
return "Usando Nintendo Network";
case nn::swkbd::LanguageType::German:
return "Nutze Nintendo Network";
}
return get_config_strings(language).using_nintendo_network.data();
}
static const char *get_pretendo_message() {
static const char *get_pretendo_message(inkay_language language) {
// TL note: "Pretendo Network" is also a proper noun - though "Pretendo" alone can refer to us as a project
// TL note: "Using" instead of "Connected" is deliberate - we don't know if a successful connection exists, we are
// only specifying what we'll *attempt* to connect to
switch (get_system_language()) {
case nn::swkbd::LanguageType::English:
default:
return "Using Pretendo Network";
case nn::swkbd::LanguageType::Spanish:
return "Usando Pretendo Network";
case nn::swkbd::LanguageType::French:
return "Sur Pretendo Network";
case nn::swkbd::LanguageType::Italian:
return "Usando Pretendo Network";
case nn::swkbd::LanguageType::German:
return "Nutze Pretendo Network";
return get_config_strings(language).using_pretendo_network.data();
}
static void Inkay_SetPluginRunning() {
Config::plugin_is_loaded = true;
}
static InkayStatus Inkay_GetStatus() {
if (!Config::initialized)
return InkayStatus::Uninitialized;
if (Config::connect_to_network) {
return InkayStatus::Pretendo;
} else {
return InkayStatus::Nintendo;
}
}
INITIALIZE_PLUGIN() {
WHBLogCafeInit();
WHBLogUdpInit();
static void Inkay_Initialize(bool apply_patches, bool show_startup_toast, inkay_language language) {
if (Config::initialized)
return;
Config::Init();
Config::show_startup_toast = show_startup_toast;
auto res = Mocha_InitLibrary();
if (res != MOCHA_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Mocha init failed with code %d!", res);
if (Config::block_initialize) {
ShowNotification("Cannot load Inkay while the system is running. Please restart the console");
return;
}
if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed");
}
//get os version
MCPSystemVersion os_version;
int mcp = MCP_Open();
int ret = MCP_GetSystemVersion(mcp, &os_version);
if (ret < 0) {
DEBUG_FUNCTION_LINE("getting system version failed (%d/%d)!", mcp, ret);
os_version = (MCPSystemVersion) {
.major = 5, .minor = 5, .patch = 5, .region = 'E'
};
}
DEBUG_FUNCTION_LINE_VERBOSE("Running on %d.%d.%d%c",
os_version.major, os_version.minor, os_version.patch, os_version.region
);
// if using pretendo then (try to) apply the ssl patches
if (Config::connect_to_network) {
if (is555(os_version)) {
if (apply_patches) {
Config::connect_to_network = true;
if (is555(get_console_os_version())) {
Mocha_IOSUKernelWrite32(0xE1019F78, 0xE3A00001); // mov r0, #1
} else {
Mocha_IOSUKernelWrite32(0xE1019E84, 0xE3A00001); // mov r0, #1
@ -183,21 +170,50 @@ INITIALIZE_PLUGIN() {
DEBUG_FUNCTION_LINE_VERBOSE("Pretendo URL and NoSSL patches applied successfully.");
ShowNotification(get_pretendo_message());
if (Config::show_startup_toast) {
ShowNotification(get_pretendo_message(language));
}
Config::initialized = true;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Pretendo URL and NoSSL patches skipped.");
ShowNotification(get_nintendo_network_message());
if(Config::show_startup_toast) {
ShowNotification(get_nintendo_network_message(language));
}
Config::initialized = true;
return;
}
MCP_Close(mcp);
if (FunctionPatcher_InitLibrary() == FUNCTION_PATCHER_RESULT_SUCCESS) {
patchDNS();
patchEshop();
patchOlvApplet();
patchAccountSettings();
install_matchmaking_patches();
} else {
DEBUG_FUNCTION_LINE("FunctionPatcher_InitLibrary failed");
}
}
DEINITIALIZE_PLUGIN() {
WUMS_INITIALIZE() {
WHBLogCafeInit();
WHBLogUdpInit();
if (const auto res = Mocha_InitLibrary(); res != MOCHA_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Mocha init failed with code %d!", res);
return;
}
if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed");
}
}
WUMS_DEINITIALIZE() {
unpatchDNS();
unpatchEshop();
unpatchOlvApplet();
unpatchAccountSettings();
remove_matchmaking_patches();
Mocha_DeInitLibrary();
@ -208,14 +224,39 @@ DEINITIALIZE_PLUGIN() {
WHBLogUdpDeinit();
}
ON_APPLICATION_START() {
WUMS_APPLICATION_STARTS() {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay " INKAY_VERSION " starting up...\n");
// Reset plugin loaded flag
Config::plugin_is_loaded = false;
}
WUMS_ALL_APPLICATION_STARTS_DONE() {
// we need to do the patches here because otherwise the Config::connect_to_network flag might be set yet
setup_olv_libs();
peertopeer_patch();
matchmaking_notify_titleswitch();
patchAccountSettings();
hotpatchAccountSettings();
if (Config::initialized && !Config::plugin_is_loaded) {
DEBUG_FUNCTION_LINE("Inkay is running but the plugin got unloaded");
if (!Config::block_initialize) {
ShowNotification("Inkay module is still running. Please restart the console");
}
Config::shown_warning = true;
} else if (!Config::initialized && !Config::shown_warning) {
DEBUG_FUNCTION_LINE("Inkay module not initialized");
ShowNotification("Inkay module was not initialized. Ensure you have the Inkay plugin loaded");
Config::shown_warning = true;
}
if (!Config::initialized) {
Config::block_initialize = true;
}
}
ON_APPLICATION_ENDS() {
WUMS_APPLICATION_ENDS() {
}
WUMS_EXPORT_FUNCTION(Inkay_Initialize);
WUMS_EXPORT_FUNCTION(Inkay_GetStatus);
WUMS_EXPORT_FUNCTION(Inkay_SetPluginRunning);

View file

@ -21,13 +21,15 @@
#include "olv_urls.h"
#include "utils/logger.h"
#include "utils/replace_mem.h"
#include "inkay_config.h"
#include <function_patcher/function_patching.h>
#include <wups.h>
#include <optional>
#include <coreinit/debug.h>
#include <coreinit/filesystem.h>
#include <coreinit/title.h>
#include <nsysnet/nssl.h>
#include <vector>
#include <optional>
#include "ca_pem.h" // generated at buildtime
@ -35,25 +37,40 @@
#define ACCOUNT_SETTINGS_TID_U 0x000500101004B100
#define ACCOUNT_SETTINGS_TID_E 0x000500101004B200
const char whitelist_original[] = {
0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x2E,
0x6E, 0x69, 0x6E, 0x74, 0x65, 0x6E, 0x64, 0x6F, 0x2E, 0x6E, 0x65, 0x74
struct account_settings_allowlist {
char scheme[16];
char domain[128];
char path[128]; // unverified
uint32_t flags;
};
const char whitelist_new[] = {
0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x2E,
0x70, 0x72, 0x65, 0x74, 0x65, 0x6E, 0x64, 0x6F, 0x2E, 0x63, 0x63, 0x00
constexpr struct account_settings_allowlist original_entry = {
.scheme = "https",
.domain = "account.nintendo.net",
.path = "",
.flags = 0x01010101,
};
const char wave_original[] = "saccount.nintendo.net";
constexpr struct account_settings_allowlist new_entry = {
.scheme = "https",
.domain = "account." NETWORK_BASEURL,
.path = "",
.flags = 0x01010101,
};
constexpr char wave_original[] = "saccount.nintendo.net";
const char wave_new[] = "saccount.pretendo.cc";
constexpr char wave_new[] = "saccount." NETWORK_BASEURL;
bool isAccountSettingsTitle();
static bool isAccountSettingsTitle() {
return (OSGetTitleID() != 0 && (
OSGetTitleID() == ACCOUNT_SETTINGS_TID_J ||
OSGetTitleID() == ACCOUNT_SETTINGS_TID_U ||
OSGetTitleID() == ACCOUNT_SETTINGS_TID_E
));
}
static std::optional<FSFileHandle> rootca_pem_handle{};
std::vector<PatchedFunctionHandle> account_patches;
DECL_FUNCTION(int, FSOpenFile_accSettings, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle,
int error) {
@ -62,7 +79,7 @@ DECL_FUNCTION(int, FSOpenFile_accSettings, FSClient *client, FSCmdBlock *block,
}
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE("Inkay: account settings patches skipped.");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: account settings patches skipped.");
return real_FSOpenFile_accSettings(client, block, path, mode, handle, error);
}
@ -70,7 +87,7 @@ DECL_FUNCTION(int, FSOpenFile_accSettings, FSClient *client, FSCmdBlock *block,
if (strcmp("vol/content/browser/rootca.pem", path) == 0) {
int ret = real_FSOpenFile_accSettings(client, block, path, mode, handle, error);
rootca_pem_handle = *handle;
DEBUG_FUNCTION_LINE("Inkay: Found account settings CA, replacing...");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Found account settings CA, replacing...");
return ret;
}
return real_FSOpenFile_accSettings(client, block, path, mode, handle, error);
@ -102,39 +119,52 @@ DECL_FUNCTION(FSStatus, FSCloseFile_accSettings, FSClient *client, FSCmdBlock *b
return real_FSCloseFile_accSettings(client, block, handle, errorMask);
}
bool isAccountSettingsTitle() {
return (OSGetTitleID() != 0 && (
OSGetTitleID() == ACCOUNT_SETTINGS_TID_J ||
OSGetTitleID() == ACCOUNT_SETTINGS_TID_U ||
OSGetTitleID() == ACCOUNT_SETTINGS_TID_E
));
bool patchAccountSettings() {
account_patches.reserve(3);
auto add_patch = [](function_replacement_data_t repl, const char *name) {
PatchedFunctionHandle handle = 0;
if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Inkay/Account: Failed to patch %s!", name);
}
account_patches.push_back(handle);
};
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSOpenFile_accSettings, LIBRARY_COREINIT, FSOpenFile, FP_TARGET_PROCESS_GAME), "FSOpenFile_accSettings");
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSReadFile_accSettings, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_GAME), "FSReadFile_accSettings");
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSCloseFile_accSettings, LIBRARY_COREINIT, FSCloseFile, FP_TARGET_PROCESS_GAME), "FSCloseFile_accSettings");
return true;
}
bool patchAccountSettings() {
bool hotpatchAccountSettings() {
if(!isAccountSettingsTitle()) {
return false;
}
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE("Inkay: account settings patches skipped.");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: account settings patches skipped.");
return false;
}
DEBUG_FUNCTION_LINE("Inkay: hewwo account settings!\n");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: hewwo account settings!\n");
if (!replace(0x10000000, 0x10000000, wave_original, sizeof(wave_original), wave_new, sizeof(wave_new))) {
DEBUG_FUNCTION_LINE("Inkay: We didn't find the url /)>~<(\\");
return false;
}
if (!replace(0x10000000, 0x10000000, whitelist_original, sizeof(whitelist_original), whitelist_new, sizeof(whitelist_new))) {
if (!replace(0x10000000, 0x10000000, (const char *)&original_entry, sizeof(original_entry), (const char *)&new_entry, sizeof(new_entry))) {
DEBUG_FUNCTION_LINE("Inkay: We didn't find the whitelist /)>~<(\\");
return false;
}
return true;
}
WUPS_MUST_REPLACE_FOR_PROCESS(FSOpenFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile, WUPS_FP_TARGET_PROCESS_GAME);
WUPS_MUST_REPLACE_FOR_PROCESS(FSReadFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSReadFile, WUPS_FP_TARGET_PROCESS_GAME);
WUPS_MUST_REPLACE_FOR_PROCESS(FSCloseFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSCloseFile, WUPS_FP_TARGET_PROCESS_GAME);
void unpatchAccountSettings() {
for (const auto handle: account_patches) {
FunctionPatcher_RemoveFunctionPatch(handle);
}
account_patches.clear();
}

View file

@ -15,6 +15,6 @@
#pragma once
bool isAccountSettingsTitle();
bool patchAccountSettings();
bool patchAccountSettings();
bool hotpatchAccountSettings();
void unpatchAccountSettings();

View file

@ -15,17 +15,21 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <wups.h>
#include <netdb.h>
#include "utils/logger.h"
#include "config.h"
#include "utils/logger.h"
#include "inkay_config.h"
#include <array>
#include <vector>
#include <function_patcher/function_patching.h>
const std::pair<const char *, const char *> dns_replacements[] = {
std::vector<PatchedFunctionHandle> dns_patches;
constexpr std::pair<const char *, const char *> dns_replacements[] = {
// NNCS servers
{ "nncs1.app.nintendowifi.net", "nncs1.app.pretendo.cc" },
{ "nncs2.app.nintendowifi.net", "nncs2.app.pretendo.cc" },
{ "nncs1.app.nintendowifi.net", "nncs1.app." NETWORK_BASEURL },
{ "nncs2.app.nintendowifi.net", "nncs2.app." NETWORK_BASEURL },
};
static const char * replace_dns_name(const char *dns_name) {
@ -42,10 +46,31 @@ static const char * replace_dns_name(const char *dns_name) {
DECL_FUNCTION(struct hostent *, gethostbyname, const char *dns_name) {
return real_gethostbyname(replace_dns_name(dns_name));
}
// might need a WUPS_MUST_REPLACE_FOR_PROCESS for Friends
WUPS_MUST_REPLACE(gethostbyname, WUPS_LOADER_LIBRARY_NSYSNET, gethostbyname);
DECL_FUNCTION(int, getaddrinfo, const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
return real_getaddrinfo(replace_dns_name(node), service, hints, res);
}
WUPS_MUST_REPLACE(getaddrinfo, WUPS_LOADER_LIBRARY_NSYSNET, getaddrinfo);
void patchDNS() {
dns_patches.reserve(2);
auto add_patch = [](function_replacement_data_t repl, const char *name) {
PatchedFunctionHandle handle = 0;
if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Inkay/DNS: Failed to patch %s!", name);
}
dns_patches.push_back(handle);
};
// might need a REPLACE_FUNCTION_FOR_PROCESS for Friends
add_patch(REPLACE_FUNCTION(gethostbyname, LIBRARY_NSYSNET, gethostbyname), "gethostbyname");
add_patch(REPLACE_FUNCTION(getaddrinfo, LIBRARY_NSYSNET, getaddrinfo), "getaddrinfo");
}
void unpatchDNS() {
for (auto handle: dns_patches) {
FunctionPatcher_RemoveFunctionPatch(handle);
}
dns_patches.clear();
}

20
src/patches/dns_hooks.h Normal file
View file

@ -0,0 +1,20 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
void patchDNS();
void unpatchDNS();

View file

@ -20,8 +20,10 @@
#include "olv_urls.h"
#include "utils/logger.h"
#include "utils/replace_mem.h"
#include "inkay_config.h"
#include <wups.h>
#include <vector>
#include <function_patcher/function_patching.h>
#include <optional>
#include <coreinit/debug.h>
#include <coreinit/filesystem.h>
@ -29,31 +31,39 @@
#include "ca_pem.h" // generated at buildtime
const char wave_original[] = "https://ninja.wup.shop.nintendo.net/ninja/wood_index.html?";
const char wave_new[] = "http://samurai.wup.shop.pretendo.cc/ninja/wood_index.html?";
constexpr char wave_original[] = "https://ninja.wup.shop.nintendo.net/ninja/wood_index.html?";
constexpr char wave_new[] = "http://samurai.wup.shop." NETWORK_BASEURL "/ninja/wood_index.html?";
const char whitelist_original[] = {
0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x75, 0x72, 0x61, 0x69, 0x2E,
0x77, 0x75, 0x70, 0x2E, 0x73, 0x68, 0x6F, 0x70, 0x2E, 0x6E, 0x69, 0x6E,
0x74, 0x65, 0x6E, 0x64, 0x6F, 0x2E, 0x6E, 0x65, 0x74
struct eshop_allowlist {
char scheme[16];
char domain[128];
char path[128]; // unverified
unsigned char flags[5];
};
const char whitelist_new[] = {
0x68, 0x74, 0x74, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x75, 0x72, 0x61, 0x69, 0x2E,
0x77, 0x75, 0x70, 0x2E, 0x73, 0x68, 0x6F, 0x70, 0x2E, 0x70, 0x72, 0x65,
0x74, 0x65, 0x6E, 0x64, 0x6F, 0x2E, 0x63, 0x63, 0x00
constexpr struct eshop_allowlist original_entry = {
.scheme = "https",
.domain = "samurai.wup.shop.nintendo.net",
.path = "",
.flags = {1, 1, 1, 1, 0},
};
constexpr struct eshop_allowlist new_entry = {
.scheme = "http",
.domain = "samurai.wup.shop." NETWORK_BASEURL,
.path = "",
.flags = {1, 1, 1, 1, 0},
};
static std::optional<FSFileHandle> rootca_pem_handle{};
std::vector<PatchedFunctionHandle> eshop_patches;
DECL_FUNCTION(int, FSOpenFile_eShop, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle,
int error) {
const char *initialOma = "vol/content/initial.oma";
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE("Inkay: eShop patches skipped.");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: eShop patches skipped.");
return real_FSOpenFile_eShop(client, block, path, mode, handle, error);
}
@ -61,19 +71,19 @@ DECL_FUNCTION(int, FSOpenFile_eShop, FSClient *client, FSCmdBlock *block, char *
//below is a hacky (yet functional!) way to get Inkay to redirect URLs from the Miiverse applet
//we do it when loading this file since it should only load once, preventing massive lag spikes as it searches all of MEM2 xD
DEBUG_FUNCTION_LINE("Inkay: hewwo eShop!\n");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: hewwo eShop!\n");
if (!replace(0x10000000, 0x10000000, wave_original, sizeof(wave_original), wave_new, sizeof(wave_new)))
DEBUG_FUNCTION_LINE("Inkay: We didn't find the url /)>~<(\\");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: We didn't find the url /)>~<(\\");
if (!replace(0x10000000, 0x10000000, whitelist_original, sizeof(whitelist_original), whitelist_new, sizeof(whitelist_new)))
DEBUG_FUNCTION_LINE("Inkay: We didn't find the whitelist /)>~<(\\");
if (!replace(0x10000000, 0x10000000, (const char *)&original_entry, sizeof(original_entry), (const char *)&new_entry, sizeof(new_entry)))
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: We didn't find the whitelist /)>~<(\\");
// Check for root CA file and take note of its handle
} else if (strcmp("vol/content/browser/rootca.pem", path) == 0) {
int ret = real_FSOpenFile_eShop(client, block, path, mode, handle, error);
rootca_pem_handle = *handle;
DEBUG_FUNCTION_LINE("Inkay: Found eShop CA, replacing...");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Found eShop CA, replacing...");
return ret;
}
@ -102,6 +112,25 @@ DECL_FUNCTION(FSStatus, FSCloseFile_eShop, FSClient *client, FSCmdBlock *block,
return real_FSCloseFile_eShop(client, block, handle, errorMask);
}
WUPS_MUST_REPLACE_FOR_PROCESS(FSOpenFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile, WUPS_FP_TARGET_PROCESS_ESHOP);
WUPS_MUST_REPLACE_FOR_PROCESS(FSReadFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSReadFile, WUPS_FP_TARGET_PROCESS_ESHOP);
WUPS_MUST_REPLACE_FOR_PROCESS(FSCloseFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSCloseFile, WUPS_FP_TARGET_PROCESS_ESHOP);
void patchEshop() {
eshop_patches.reserve(3);
auto add_patch = [](function_replacement_data_t repl, const char *name) {
PatchedFunctionHandle handle = 0;
if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Inkay/eShop: Failed to patch %s!", name);
}
eshop_patches.push_back(handle);
};
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSOpenFile_eShop, LIBRARY_COREINIT, FSOpenFile, FP_TARGET_PROCESS_ESHOP), "FSOpenFile_eShop");
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSReadFile_eShop, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_ESHOP), "FSReadFile_eShop");
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSCloseFile_eShop, LIBRARY_COREINIT, FSCloseFile, FP_TARGET_PROCESS_ESHOP), "FSCloseFile_eShop");
}
void unpatchEshop() {
for (auto handle: eshop_patches) {
FunctionPatcher_RemoveFunctionPatch(handle);
}
eshop_patches.clear();
}

View file

@ -0,0 +1,20 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
void patchEshop();
void unpatchEshop();

View file

@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "game_matchmaking.h"
#include "config.h"
#include "game_matchmaking.h"
#include "utils/logger.h"
#include "ini.h"
@ -58,7 +58,7 @@ static int handler(void *user, const char *section, const char *name, const char
static void check_modpack() {
modpack mod;
if (ini_parse("fs:/vol/content/pretendo.ini", handler, &mod)) {
DEBUG_FUNCTION_LINE("Inkay/MK8: Doesn't look like a modpack");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay/MK8: Doesn't look like a modpack");
}
DEBUG_FUNCTION_LINE("Inkay/MK8: Playing %s (%08x)", mod.name.c_str(), mod.dlc_id);
@ -72,7 +72,7 @@ DECL_FUNCTION(void, mk8_MatchmakeSessionSearchCriteria_SetAttribute, void *_this
const int dlc_id = dlc_modpack->dlc_id;
if (dlc_id != -1) {
DEBUG_FUNCTION_LINE("Inkay/MK8: Searching for %s session (%08x)", dlc_modpack->name.c_str(), dlc_id);
DEBUG_FUNCTION_LINE_VERBOSE("Inkay/MK8: Searching for %s session (%08x)", dlc_modpack->name.c_str(), dlc_id);
attributeValue = dlc_id;
}
}
@ -86,7 +86,7 @@ DECL_FUNCTION(void, mk8_MatchmakeSession_SetAttribute, void *_this, uint32_t att
const int dlc_id = dlc_modpack->dlc_id;
if (dlc_id != -1) {
DEBUG_FUNCTION_LINE("Inkay/MK8: Creating %s session (%08x)", dlc_modpack->name.c_str(), dlc_id);
DEBUG_FUNCTION_LINE_VERBOSE("Inkay/MK8: Creating %s session (%08x)", dlc_modpack->name.c_str(), dlc_id);
attributeValue = dlc_id;
}
}

View file

@ -0,0 +1,131 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include <coreinit/title.h>
#include <coreinit/dynload.h>
#include "game_peertopeer.h"
#include "config.h"
#include "sysconfig.h"
#include "utils/logger.h"
#include "utils/rpl_info.h"
#include "utils/replace_mem.h"
#include <optional>
#include <algorithm>
#include <string_view>
using namespace std::string_view_literals;
static struct {
std::array<uint64_t, 3> tid;
uint16_t version;
uint32_t min_port_addr;
uint32_t max_port_addr;
std::string_view rpx;
} generic_patch_games[] = {
{
// MARIO KART 8
{0x00050000'1010ec00, 0x00050000'1010ed00, 0x00050000'1010eb00},
81,
0x101a9a52,
0x101a9a54,
"Turbo.rpx"sv,
},
{
// Splatoon
{0x00050000'10176900, 0x00050000'10176a00, 0x00050000'10162b00},
288,
0x101e8952,
0x101e8954,
"Gambit.rpx"sv,
},
};
static void generic_peertopeer_patch() {
uint64_t tid = OSGetTitleID();
uint16_t title_version = 0;
if (const auto version_opt = get_current_title_version(); !version_opt) {
DEBUG_FUNCTION_LINE("Failed to detect current title version");
return;
} else {
title_version = *version_opt;
DEBUG_FUNCTION_LINE("Title version detected: %d", title_version);
}
for (const auto &patch: generic_patch_games) {
if (std::ranges::find(patch.tid, tid) == patch.tid.end()) continue;
std::optional<OSDynLoad_NotifyData> game = search_for_rpl(patch.rpx);
if (!game) {
DEBUG_FUNCTION_LINE("Couldn't find game rpx! (%s)", patch.rpx.data());
return;
}
if (title_version != patch.version) {
DEBUG_FUNCTION_LINE("Unexpected title version. Expected %d but got %d (%s)", patch.version, title_version,
patch.rpx.data());
continue;
}
auto port = get_console_peertopeer_port();
DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, game->textAddr);
auto target = (uint16_t *)rpl_addr(*game, patch.min_port_addr);
replace_unsigned<uint16_t>(target, 0xc000, port);
target = (uint16_t *)rpl_addr(*game, patch.max_port_addr);
replace_unsigned<uint16_t>(target, 0xffff, port);
break;
}
}
static void minecraft_peertopeer_patch() {
std::optional<OSDynLoad_NotifyData> minecraft = search_for_rpl("Minecraft.Client.rpx"sv);
if (!minecraft) {
DEBUG_FUNCTION_LINE("Couldn't find minecraft rpx!");
return;
}
if (const auto version_opt = get_current_title_version(); !version_opt || *version_opt != 688) {
DEBUG_FUNCTION_LINE("Wrong mincecraft version detected");
return;
}
auto port = get_console_peertopeer_port();
DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, minecraft->textAddr);
auto target_func = (uint32_t *)rpl_addr(*minecraft, 0x03579530);
replace_instruction(&target_func[0], 0x3c600001, 0x3c600000); // li r3, 0
replace_instruction(&target_func[1], 0x3863c000, 0x60630000 | port); // ori r3, r3, port
// blr
target_func = (uint32_t *)rpl_addr(*minecraft, 0x0357953c);
replace_instruction(&target_func[0], 0x3c600001, 0x3c600000); // li r3, 0
replace_instruction(&target_func[1], 0x3863ffff, 0x60630000 | port); // ori r3, r3, port
// blr
}
void peertopeer_patch() {
if (!Config::connect_to_network) {
return;
}
uint64_t tid = OSGetTitleID();
if (tid == 0x00050000'101D7500 || // EUR
tid == 0x00050000'101D9D00 || // USA
tid == 0x00050000'101DBE00) { // JPN
minecraft_peertopeer_patch();
} else {
generic_peertopeer_patch();
}
}

View file

@ -0,0 +1,16 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
void peertopeer_patch();

View file

@ -21,24 +21,36 @@
#include "utils/logger.h"
#include "utils/replace_mem.h"
#include <wups.h>
#include <vector>
#include <optional>
#include <coreinit/debug.h>
#include <coreinit/filesystem.h>
#include <nsysnet/nssl.h>
#include <function_patcher/function_patching.h>
#include "ca_pem.h" // generated at buildtime
const char wave_original[] = {
0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2E, 0x6E, 0x69, 0x6E, 0x74, 0x65, 0x6E, 0x64,
0x6F, 0x2E, 0x6E, 0x65, 0x74
struct olv_allowlist {
char scheme[16];
char domain[128];
char path[128]; // unverified
unsigned char flags[5];
};
const char wave_new[] = {
0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2E, 0x70, 0x72, 0x65, 0x74, 0x65, 0x6E, 0x64,
0x6F, 0x2E, 0x63, 0x63, 0x00
constexpr struct olv_allowlist original_entry = {
.scheme = "https",
.domain = ".nintendo.net",
.path = "",
.flags = {1, 1, 1, 1, 1},
};
constexpr struct olv_allowlist new_entry = {
.scheme = "https",
.domain = ".spbr.net",
.path = "",
.flags = {1, 1, 1, 1, 1},
};
const unsigned char miiverse_green_highlight[] = {
0x82, 0xff, 0x05, 0xff, 0x82, 0xff, 0x05, 0xff, 0x1d, 0xff, 0x04, 0xff, 0x1d, 0xff, 0x04, 0xff
};
@ -65,13 +77,15 @@ const replacement replacements[] = {
};
static std::optional<FSFileHandle> rootca_pem_handle{};
static std::optional<FSFileHandle> valid_tld_handle{};
std::vector<PatchedFunctionHandle> olv_patches;
DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle,
int error) {
const char *initialOma = "vol/content/initial.oma";
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE("Inkay: Miiverse patches skipped.");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Miiverse patches skipped.");
return real_FSOpenFile(client, block, path, mode, handle, error);
}
@ -80,17 +94,25 @@ DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path,
//we do it when loading this file since it should only load once, preventing massive lag spikes as it searches all of MEM2 xD
//WHBLogUdpInit();
DEBUG_FUNCTION_LINE("Inkay: hewwo!\n");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: hewwo!\n");
auto olv_ok = setup_olv_libs();
// Patch applet binary too
if (olv_ok)
replace(0x10000000, 0x10000000, wave_original, sizeof(wave_original), wave_new, sizeof(wave_new));
replace(0x10000000, 0x10000000, (const char *)&original_entry, sizeof(original_entry), (const char *)&new_entry, sizeof(new_entry));
// Check for root CA file and take note of its handle
} else if (strcmp("vol/content/browser/rootca.pem", path) == 0) {
int ret = real_FSOpenFile(client, block, path, mode, handle, error);
rootca_pem_handle = *handle;
DEBUG_FUNCTION_LINE("Inkay: Found Miiverse CA, replacing...");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Found Miiverse CA, replacing...");
return ret;
}
else if (strcmp("vol/content/browser/effective_tld_names.dat", path) == 0)
{
// we patch the TLD because .cafe isnt on the list
int ret = real_FSOpenFile(client, block, path, mode, handle, error);
valid_tld_handle = *handle;
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Found Miiverse TLD List, patching...");
return ret;
}
@ -98,52 +120,83 @@ DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path,
}
DECL_FUNCTION(FSStatus, FSReadFile, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count,
FSFileHandle handle, uint32_t unk1, uint32_t flags) {
if (size != 1) {
DEBUG_FUNCTION_LINE("Inkay: Miiverse CA replacement failed!");
FSFileHandle handle, uint32_t unk1, uint32_t flags)
{
if (size != 1)
{
DEBUG_FUNCTION_LINE("Inkay: Miiverse CA/LTD replacement failed!");
}
if (rootca_pem_handle && *rootca_pem_handle == handle) {
strlcpy((char *) buffer, (const char *) ca_pem, size * count);
if (rootca_pem_handle && *rootca_pem_handle == handle)
{
strlcpy((char *)buffer, (const char *)ca_pem, size * count);
//this can't be done above (in the FSOpenFile hook) since it's not loaded yet.
//the hardcoded offsets suck but they really are at Random Places In The Heap
replaceBulk(0x11000000, 0x02000000, replacements);
return (FSStatus) count;
// this can't be done above (in the FSOpenFile hook) since it's not loaded yet.
// the hardcoded offsets suck but they really are at Random Places In The Heap
return (FSStatus)count;
}
else if (valid_tld_handle && *valid_tld_handle == handle)
{
// we patch the tld "aero"...
FSStatus ret = real_FSReadFile(
client, block, buffer, size, count, handle, unk1, flags);
if (ret > 0)
{
uint32_t totalSize = size * count;
char *buf = (char *)buffer;
for (uint32_t i = 0; i + 4 <= totalSize; i++)
{
if (memcmp(&buf[i], "aero", 4) == 0)
{
memcpy(&buf[i], "cafe", 4);
DEBUG_FUNCTION_LINE_VERBOSE(
"Inkay: patched TLD aero -> cafe");
}
}
}
return ret;
}
return real_FSReadFile(client, block, buffer, size, count, handle, unk1, flags);
}
DECL_FUNCTION(FSStatus, FSCloseFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
if (handle == rootca_pem_handle) {
rootca_pem_handle.reset();
}
if (handle == valid_tld_handle)
{
valid_tld_handle.reset();
}
return real_FSCloseFile(client, block, handle, errorMask);
}
DECL_FUNCTION(uint32_t, NSSLExportInternalServerCertificate, NSSLServerCertId cert, int unk, void *unk2, void *unk3) {
if (cert == NSSL_SERVER_CERT_THAWTE_PREMIUM_SERVER_CA) { // Martini patches
OSFatal("[598-0069] Please uninstall Martini patches to continue.\n" \
"See pretendo.network/docs/search for more info.\n\n" \
"Hold the POWER button for 4 seconds to shut down.\n\n"
" .\n"
".---------.'---.\n"
"'. : .'\n"
" '. .::: .'\n"
" '.'::'.'\n"
" '||'\n"
" ||\n"
" ||\n"
"mrz ||\n"
" ---====---");
}
return real_NSSLExportInternalServerCertificate(cert, unk, unk2, unk3);
void patchOlvApplet() {
olv_patches.reserve(3);
auto add_patch = [](function_replacement_data_t repl, const char *name) {
PatchedFunctionHandle handle = 0;
if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Inkay/OLV: Failed to patch %s!", name);
}
olv_patches.push_back(handle);
};
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSOpenFile, LIBRARY_COREINIT, FSOpenFile, FP_TARGET_PROCESS_MIIVERSE), "FSOpenFile");
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSReadFile, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_MIIVERSE), "FSReadFile");
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSCloseFile, LIBRARY_COREINIT, FSCloseFile, FP_TARGET_PROCESS_MIIVERSE), "FSCloseFile");
}
WUPS_MUST_REPLACE_FOR_PROCESS(FSOpenFile, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile, WUPS_FP_TARGET_PROCESS_MIIVERSE);
WUPS_MUST_REPLACE_FOR_PROCESS(FSReadFile, WUPS_LOADER_LIBRARY_COREINIT, FSReadFile, WUPS_FP_TARGET_PROCESS_MIIVERSE);
WUPS_MUST_REPLACE_FOR_PROCESS(FSCloseFile, WUPS_LOADER_LIBRARY_COREINIT, FSCloseFile, WUPS_FP_TARGET_PROCESS_MIIVERSE);
WUPS_MUST_REPLACE_FOR_PROCESS(NSSLExportInternalServerCertificate, WUPS_LOADER_LIBRARY_NSYSNET,
NSSLExportInternalServerCertificate, WUPS_FP_TARGET_PROCESS_MIIVERSE);
void unpatchOlvApplet() {
for (auto handle: olv_patches) {
FunctionPatcher_RemoveFunctionPatch(handle);
}
olv_patches.clear();
}

20
src/patches/olv_applet.h Normal file
View file

@ -0,0 +1,20 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
void patchOlvApplet();
void unpatchOlvApplet();

View file

@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "olv_urls.h"
#include "config.h"
#include "olv_urls.h"
#include "utils/logger.h"
#include "utils/replace_mem.h"
@ -51,20 +51,20 @@ bool path_is_olv(const char* path) {
void new_rpl_loaded(OSDynLoad_Module module, void* ctx, OSDynLoad_NotifyReason reason, OSDynLoad_NotifyData* rpl) {
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE("Inkay: Miiverse patches skipped.");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Miiverse patches skipped.");
return;
}
// Loaded olv?
if (reason != OS_DYNLOAD_NOTIFY_LOADED) return;
if (!path_is_olv(rpl->name)) return;
if (!rpl->name || !path_is_olv(rpl->name)) return;
replace(rpl->dataAddr, rpl->dataSize, original_url, sizeof(original_url), new_url, sizeof(new_url));
}
bool setup_olv_libs() {
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE("Inkay: Miiverse patches skipped.");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Miiverse patches skipped.");
return false;
}
@ -72,7 +72,7 @@ bool setup_olv_libs() {
auto olvLoaded = check_olv_libs();
if (!olvLoaded) {
DEBUG_FUNCTION_LINE("Inkay: no olv, quitting for now\n");
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: no olv, quitting for now\n");
return false;
}

View file

@ -15,11 +15,12 @@
#pragma once
#include <cstdlib>
#include "inkay_config.h"
const char original_url[] = "discovery.olv.nintendo.net/v1/endpoint";
const char new_url[] = "discovery.olv.pretendo.cc/v1/endpoint";
constexpr char original_url[] = "discovery.olv.nintendo.net/v1/endpoint";
constexpr char new_url[] = "discovery.olv.spbr.net/v1/endpoint";
_Static_assert(sizeof(original_url) > sizeof(new_url),
_Static_assert(sizeof(original_url) >= sizeof(new_url),
"new_url too long! Must be less than 38chars.");
bool setup_olv_libs();

View file

@ -1,28 +1,28 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include <whb/log.h>
#include <whb/log_module.h>
#include <whb/log_cafe.h>
#include <whb/log_udp.h>
#ifdef __cplusplus
extern "C" {
#endif
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \
OSFatal_printf("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
OSFatal_printf("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \
WHBLogPrintf("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
WHBLogPrintf("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0);
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \
WHBLogWritef("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
WHBLogWritef("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0);
#ifdef DEBUG

View file

@ -22,6 +22,7 @@
#include <kernel/kernel.h>
#include <coreinit/memorymap.h>
#include <algorithm>
#include <coreinit/cache.h>
bool replace(uint32_t start, uint32_t size, const char *original_val, size_t original_val_sz, const char *new_val,
size_t new_val_sz) {
@ -65,7 +66,36 @@ void replaceBulk(uint32_t start, uint32_t size, std::span<const replacement> rep
}
}
}
#ifdef DEBUG
for (auto c: counts) {
DEBUG_FUNCTION_LINE("replaced %d times", c);
}
#endif
}
template <typename U>
requires std::integral<U>
bool replace_unsigned(U *addr, U original_value, U new_value) {
if (*addr != original_value) return false;
KernelCopyData(
OSEffectiveToPhysical((uint32_t) addr),
OSEffectiveToPhysical((uint32_t) &new_value),
sizeof(new_value)
);
DCFlushRange(addr, sizeof(new_value));
return *addr == new_value;
}
template bool replace_unsigned<uint64_t>(uint64_t *, uint64_t, uint64_t);
template bool replace_unsigned<uint32_t>(uint32_t *, uint32_t, uint32_t);
template bool replace_unsigned<uint16_t>(uint16_t *, uint16_t, uint16_t);
template bool replace_unsigned<uint8_t>(uint8_t *, uint8_t, uint8_t);
bool replace_instruction(uint32_t *inst, uint32_t original_value, uint32_t new_value) {
bool res = replace_unsigned<uint32_t>(inst, original_value, new_value);
if (!res) return res;
ICInvalidateRange(inst, sizeof(new_value));
return true;
}

View file

@ -27,3 +27,9 @@ struct replacement {
};
void replaceBulk(uint32_t start, uint32_t size, std::span<const replacement> replacements);
template <typename U>
requires std::integral<U>
bool replace_unsigned(U *addr, U original_value, U new_value);
bool replace_instruction(uint32_t *inst, uint32_t original_value, uint32_t new_value);

64
src/utils/rpl_info.cpp Normal file
View file

@ -0,0 +1,64 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "rpl_info.h"
#include <vector>
#include <algorithm>
#include <string_view>
#include <coreinit/mcp.h>
#include <coreinit/title.h>
#include "logger.h"
// if we get more than like.. two callsites for this, it should really be refactored out rather than doing a fresh
// search every time
std::optional<OSDynLoad_NotifyData> search_for_rpl(std::string_view name) {
int num_rpls = OSDynLoad_GetNumberOfRPLs();
if (!num_rpls)
return std::nullopt;
// allocates but we free it at return
std::vector<OSDynLoad_NotifyData> rpls(num_rpls);
if (!OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data()))
return std::nullopt;
auto rpl = std::find_if(rpls.begin(), rpls.end(), [=](const OSDynLoad_NotifyData &rpl) {
return std::string_view(rpl.name).ends_with(name);
});
if (rpl == rpls.end())
return std::nullopt;
return *rpl;
}
std::optional<uint16_t> get_current_title_version() {
const auto mcpHandle = MCP_Open();
MCPTitleListType titleInfo;
int32_t res = -1;
const uint64_t curTitleId = OSGetTitleID();
if ((curTitleId & 0x0000000F00000000) == 0) {
res = MCP_GetTitleInfo(mcpHandle, curTitleId | 0x0000000E00000000, &titleInfo);
}
if (res != 0) {
res = MCP_GetTitleInfo(mcpHandle, curTitleId, &titleInfo);
}
MCP_Close(mcpHandle);
if (res != 0) {
DEBUG_FUNCTION_LINE("Failed to get title version of %016llX.", curTitleId);
return {};
}
MCP_Close(mcpHandle);
const auto tmp_result = titleInfo.titleVersion; // make the compiler happy because we access a packed struct
return tmp_result;
}

30
src/utils/rpl_info.h Normal file
View file

@ -0,0 +1,30 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <optional>
#include <string_view>
#include <coreinit/dynload.h>
std::optional<OSDynLoad_NotifyData> search_for_rpl(std::string_view name);
constexpr void *rpl_addr(OSDynLoad_NotifyData rpl, uint32_t cemu_addr) {
if (cemu_addr < 0x1000'0000) {
return (void *)(rpl.textAddr + cemu_addr - 0x0200'0000);
} else {
return (void *)(rpl.dataAddr + cemu_addr - 0x1000'0000);
}
}
std::optional<uint16_t> get_current_title_version();

View file

@ -1,57 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Copyright 2020-2022 V10lator <v10lator@myway.de>
Copyright 2022 Xpl0itU <DaThinkingChair@protonmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sysconfig.h"
#include "utils/logger.h"
#include <coreinit/userconfig.h>
#include <optional>
nn::swkbd::LanguageType get_system_language() {
static std::optional <nn::swkbd::LanguageType> cached_language{};
if (cached_language) return *cached_language;
UCHandle handle = UCOpen();
if (handle >= 0) {
nn::swkbd::LanguageType language;
UCSysConfig settings __attribute__((__aligned__(0x40))) = {
.name = "cafe.language",
.access = 0,
.dataType = UC_DATATYPE_UNSIGNED_INT,
.error = UC_ERROR_OK,
.dataSize = sizeof(language),
.data = &language,
};
UCError err = UCReadSysConfig(handle, 1, &settings);
UCClose(handle);
if (err != UC_ERROR_OK) {
DEBUG_FUNCTION_LINE("Error reading UC: %d!", err);
return nn::swkbd::LanguageType::English;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("System language found: %d", language);
cached_language = language;
return language;
}
} else {
DEBUG_FUNCTION_LINE("Error opening UC: %d", handle);
return nn::swkbd::LanguageType::English;
}
}