Compare commits

...

46 Commits

Author SHA1 Message Date
Martin Michelsen 5ea3d0ad4b add missing quest files 2024-07-10 20:41:18 -07:00
Martin Michelsen 90efde7aa9 handle rare shell i/o error 2024-07-10 19:48:51 -07:00
Martin Michelsen 55f1869125 add replace sound effects code 2024-07-08 01:08:43 -07:00
Martin Michelsen b4efd90fdc replace q050 and q052 with direct backports 2024-07-07 16:39:52 -07:00
Martin Michelsen 87dd554592 remove offset comments in reassembly mode 2024-07-07 15:19:18 -07:00
Martin Michelsen 58974ae1be use JP version as default sub_version 2024-07-06 12:57:30 -07:00
Martin Michelsen 21c8bab91c handle one 6x63 data race 2024-07-06 09:54:07 -07:00
Martin Michelsen c58b37be23 add GC-GJAM quest opcodes in handler-tables 2024-07-05 21:55:16 -07:00
Martin Michelsen d3d98c44b8 clear ep3 media when switching to proxy server 2024-07-04 16:25:21 -07:00
Martin Michelsen dc2e73d198 fix item notifications for mags on GC 2024-07-04 16:24:36 -07:00
Martin Michelsen 774f9649da fix binary operator bind order 2024-07-04 16:24:18 -07:00
Martin Michelsen 093287af75 fix condition icons in enemy HP bars patch 2024-06-29 22:25:32 -07:00
Martin Michelsen 0126189cbd fix 3OJ2 and 3OJ3 item loss patches 2024-06-29 12:22:27 -07:00
Martin Michelsen c250a2dbc4 update enemy HP bars patch 2024-06-28 20:57:16 -07:00
Martin Michelsen 2ff9df19c8 don't allow language fallback for q88500 2024-06-28 14:10:06 -07:00
Martin Michelsen 528593651b update TODO.md 2024-06-28 10:13:24 -07:00
Martin Michelsen 9f073d07cd don't use member initialization 2024-06-28 10:10:39 -07:00
Martin Michelsen 4bd6ef12a9 implement $savechar on Episode 3 2024-06-28 09:48:09 -07:00
Martin Michelsen 52644695a3 fix grind limit overdraft 2024-06-26 20:00:22 -07:00
Martin Michelsen 45e619718c fix patch menu on BB 2024-06-26 19:37:21 -07:00
nolrinale 43fd979763 Fixed Coren rewards item names causing parsing issues 2024-06-26 09:36:21 -07:00
Martin Michelsen 082bc49a4d add customization segregation test 2024-06-24 00:01:09 -07:00
Martin Michelsen 4adcaa7bee skip max grind check on v3 2024-06-24 00:00:48 -07:00
Martin Michelsen 630ae0beb4 fix stack limits on DC NTE 2024-06-23 22:38:46 -07:00
Martin Michelsen 246dfd9fe0 update notes on DC NTE quest commands 2024-06-23 22:38:46 -07:00
Martin Michelsen 6f056cb1bd update proxy options 2024-06-23 22:38:46 -07:00
Martin Michelsen 9322c023da fix missing sub_version check 2024-06-23 22:38:46 -07:00
Martin Michelsen fd4719f8ec clean up comments in q88500 2024-06-23 08:38:51 -07:00
Martin Michelsen 3a22a5c489 add Ep3 codepaths to B2 enabler 2024-06-23 00:24:01 -07:00
Martin Michelsen 862b3d27da add B2 patch support on PSO Plus 2024-06-22 21:42:30 -07:00
Martin Michelsen 998664d2fb add loading screen AR code 2024-06-22 17:22:02 -07:00
Martin Michelsen 0bf2d950ac fix offsets on DCv1 item loss patches 2024-06-22 15:23:38 -07:00
Martin Michelsen 3ae5e875a1 fix comments on some quest opcodes 2024-06-22 15:23:23 -07:00
Martin Michelsen a88795d8b9 fix edge case in quest episode detection 2024-06-22 15:22:52 -07:00
Martin Michelsen 9ca1b79409 add .include directives in quest assembler 2024-06-22 15:22:32 -07:00
Martin Michelsen ce8277b96a describe 6x51 command 2024-06-22 15:20:48 -07:00
Martin Michelsen 25731eb71f add comments about UDP subcommands 2024-06-22 15:20:39 -07:00
Martin Michelsen e55963b82b specify types on some quest handlers 2024-06-22 14:22:29 -07:00
Martin Michelsen b9d9b38351 add US v1.2 quest opcodes to handler-tables 2024-06-22 09:29:40 -07:00
Martin Michelsen 782babf3ae add quest opcode names in handler-tables 2024-06-22 09:07:37 -07:00
Martin Michelsen 9869fa03c2 only send notifs for client-generated items if the game drop mode is CLIENT 2024-06-21 11:02:59 -07:00
Martin Michelsen 0ae02b0191 add websocket endpoint for rare drop stream 2024-06-21 10:59:01 -07:00
Martin Michelsen c0ea976fdc fix typo in VersionDetectGC 2024-06-21 10:59:01 -07:00
nolrinale c4bf9e7d5b Adjusted Coren rewards to match Retail server ones 2024-06-20 20:24:05 -07:00
Martin Michelsen 2e5d95d612 fix data race in 6xCA command 2024-06-19 23:31:51 -07:00
nolrinale 75b2827da9 Corrected BB english client files 2024-06-18 19:37:37 -07:00
173 changed files with 15746 additions and 1118 deletions
+5 -1
View File
@@ -357,10 +357,14 @@ Exactly which data is saved and loaded depends on the game version:
| PSO PC (v2) | Yes | Yes | No | No | No | Save only |
| PSO GC NTE | Yes | Yes | Yes | Yes | Yes | Yes |
| PSO GC (not Plus) | Yes | Yes | Yes | Yes | Yes | Yes |
| PSO GC Plus | Save only | Save only | No | No | No | Save only |
| PSO GC Plus (1) | Save only | Save only | No | No | No | Save only |
| PSO GC Ep3 (1) | No | Save only | No | No | No | Save only |
| PSO Xbox | Yes | Yes | Yes | Yes | Yes | Yes |
| PSO BB | Yes | Yes | Yes | Yes | Yes | Yes |
*Notes*:
1. *If EnableSendFunctionCallQuestNumber is enabled in config.json, then $savechar and $loadchar can save and restore all character data on these versions, just like on GC non-Plus. Episode 3 characters exist in a separate namespace; that is, you can't use $savechar and $loadchar to convert an Ep3 character to non-Ep3, or vice versa.*
## Episode 3 features
newserv supports many features unique to Episode 3:
+1 -3
View File
@@ -1,11 +1,10 @@
## General
- Make reloading happen on separate threads so compression doesn't block active clients
- Implement decrypt/encrypt actions for VMS files
- Make UI strings localizable (e.g. entries in menus, welcome message, etc.)
- Add an idle connection timeout for proxy sessions
- Clean up ItemParameterTable implementation (see comment at the top of the class definition)
- Handle MeetUserExtensions properly in 41 and C4 commands on the proxy (rewrite the embedded 19 command and store a map of )
- Handle MeetUserExtensions properly in 41 and C4 commands on the proxy (rewrite the embedded 19 command and store a map of received destinations)
## PSO DC
@@ -26,7 +25,6 @@
## PSOBB
- Test all quest item subcommands
- Figure out why Pouilly Slime EXP doesn't work
- Make server-specified rare enemies work with maps loaded by the proxy
- Implement serialization for various table types (ItemPMT, ItemPT, etc.)
+10
View File
@@ -79,6 +79,11 @@ Ep3-US => 042F9AC0 60000000
Ep3-NTE => 040C2C48 60000000
Ep3-JP => 042F8B74 60000000
(Ep1&2 USA v1.1) Change type of all loading screens
0401CA04 3BE0000X
0401CA08 48000038
Values for X: 0 = lobby/game join, 1 = quest load, 3 = pipe up, 4 = pipe down, anything else = silent black screen
(Ep3 USA) Replace loading screen A button sounds with random sounds
042F9B18 4804BB19
042F9B1C 5463063E
@@ -350,3 +355,8 @@ Note: Without a TextEnglish.pr2/pr3 patch, the menu items for these sounds will
0408E448 38000001
0408E44C 900DA62C
0408E450 4E800020
(v1.1 USA) Replace all sound effects with specified sound effect
042256E4 3F40XXXX
042256E8 635AYYYY
042256EC 4800000C
+573 -573
View File
File diff suppressed because it is too large Load Diff
+41 -11
View File
@@ -73,9 +73,9 @@ ItemLossPrevention
*** desc=Don't lose items if\nyou don't log off\nnormally
JP12------------- JP13------------- JP14------------- JP15------------- US10------------- US11------------- US12------------- EU--------------- DISASSEMBLY (US10)
801D33E4 4800004C 801D38EC 4800004C 801D3CC4 4800004C 801D39B8 4800004C 801D381C 4800004C 801D381C 4800004C 801D3A1C 4800004C 801D3ED8 4800004C b +0x0000004C /* 801D3868 */
8020010C 60000000 801FF710 60000000 801FF0FC 60000000 801FF0FC 60000000 801FFA44 60000000 801FF9E0 60000000 nop
802016CC 60000000 80200C9C 60000000 80200658 60000000 80200658 60000000 80200FD0 60000000 80200F3C 60000000 nop
801FD944 38000000 80202860 38000000 802021C4 38000000 802021C4 38000000 80202B94 38000000 80202AA8 38000000 li r0, 0x0000
801FE900 60000000 801FF174 60000000 8020010C 60000000 801FF710 60000000 801FF0FC 60000000 801FF0FC 60000000 801FFA44 60000000 801FF9E0 60000000 nop
801FFE5C 60000000 802006D0 60000000 802016CC 60000000 80200C9C 60000000 80200658 60000000 80200658 60000000 80200FD0 60000000 80200F3C 60000000 nop
802019C8 38000000 8020223C 38000000 801FD944 38000000 80202860 38000000 802021C4 38000000 802021C4 38000000 80202B94 38000000 80202AA8 38000000 li r0, 0x0000
802C2060 4800004C 802C2F98 4800004C 802C42E4 4800004C 802C3E78 4800004C 802C2A40 4800004C 802C2A84 4800004C 802C402C 4800004C 802C37C0 4800004C b +0x0000004C /* 802C2A8C */
802D0AA0 48000020 802D1A58 48000020 802D2C10 48000020 802D2938 48000020 802D1480 48000020 802D14C4 48000020 802D2AEC 48000020 802D2280 48000020 b +0x00000020 /* 802D14A0 */
@@ -754,15 +754,45 @@ Show Enemy HP Bars
EnemyHPBars
*** name=Enemy HP bars
*** desc=Show HP bars in\nenemy info windows
JP12------------- JP13------------- JP14------------- JP15------------- US10------------- US11------------- US12------------- EU--------------- DISASSEMBLY (US10)
JP12------------- JP13------------- JP14------------- JP15------------- US10------------- US11------------- US12------------- EU--------------- DISASSEMBLY (US12)
802612C4 4BFE1541 80261E9C 4BFE1349 80262EE4 4BFE0665 80262C98 4BFE1241 80261B9C 4BFE1545 80261B9C 4BFE1545 80262F5C 4BFE12B1 802627A4 4BFE12B1 bl -0x0001EABC /* 802430E0 */
804CAF00 42300000 804CE650 42300000 804D0BA0 42300000 804D0940 42300000 804CB6D0 42300000 804CBBB0 42300000 804D0218 42300000 804D0608 42300000 bdnz cr4, +0x00000000 /* 804CB6D0 */
804CAF1C FF00FF15 804CE66C FF00FF15 804D0BBC FF00FF15 804D095C FF00FF15 804CB6EC FF00FF15 804CBBCC FF00FF15 804D0234 FF00FF15 804D0624 FF00FF15 .invalid FC, 0
805CBFBC 42A00000 805D65BC 42A00000 805DDA5C 42A00000 805DD7FC 42A00000 805CC8C4 42A00000 805D38E4 42A00000 805DD104 42A00000 805D9344 42A00000 b +0x00000000 /* 805CC8C4 */
804CAE40 42640000 804CE590 42640000 804D0AE0 42640000 804D0880 42640000 804CB610 42640000 804CBAF0 42640000 804D0158 42640000 804D0548 42640000 bc 19, 4, +0x00000000 /* 804CB610 */
804CAE4C 42640000 804CE59C 42640000 804D0AEC 42640000 804D088C 42640000 804CB61C 42640000 804CBAFC 42640000 804D0164 42640000 804D0554 42640000 bc 19, 4, +0x00000000 /* 804CB61C */
804CAE58 42640000 804CE5A8 42640000 804D0AF8 42640000 804D0898 42640000 804CB628 42640000 804CBB08 42640000 804D0170 42640000 804D0560 42640000 bc 19, 4, +0x00000000 /* 804CB628 */
804CAE64 42640000 804CE5B4 42640000 804D0B04 42640000 804D08A4 42640000 804CB634 42640000 804CBB14 42640000 804D017C 42640000 804D056C 42640000 bc 19, 4, +0x00000000 /* 804CB634 */
804CAF00 42780000 804CE650 42780000 804D0BA0 42780000 804D0940 42780000 804CB6D0 42780000 804CBBB0 42780000 804D0218 42780000 804D0608 42780000
804CAF1C FF00FF15 804CE66C FF00FF15 804D0BBC FF00FF15 804D095C FF00FF15 804CB6EC FF00FF15 804CBBCC FF00FF15 804D0234 FF00FF15 804D0624 FF00FF15
805CBFBC 42C00000 805D65BC 42C00000 805DDA5C 42C00000 805DD7FC 42C00000 805CC8C4 42C00000 805D38E4 42C00000 805DD104 42C00000 805D9344 42C00000
804CAE40 42960000 804CE590 42960000 804D0AE0 42960000 804D0880 42960000 804CB610 42960000 804CBAF0 42960000 804D0158 42960000 804D0548 42960000
804CAE4C 42960000 804CE59C 42960000 804D0AEC 42960000 804D088C 42960000 804CB61C 42960000 804CBAFC 42960000 804D0164 42960000 804D0554 42960000
804CAE58 42960000 804CE5A8 42960000 804D0AF8 42960000 804D0898 42960000 804CB628 42960000 804CBB08 42960000 804D0170 42960000 804D0560 42960000
804CAE64 42960000 804CE5B4 42960000 804D0B04 42960000 804D08A4 42960000 804CB634 42960000 804CBB14 42960000 804D017C 42960000 804D056C 42960000
804CAE70 42960000 804CE5C0 42960000 804D0B10 42960000 804D08B0 42960000 804CB640 42960000 804CBB20 42960000 804D0188 42960000 804D0578 42960000
80261260 4BDAA3F1 80261E38 4BDA9819 80262E80 4BDA87D1 80262C34 4BDA8A1D 80261B38 4BDA9B19 80261B38 4BDA9B19 80262EF8 4BDA8759 80262740 4BDA8F11 bl -0x002578A8 /* 8000B650 */
80261420 4BDAA245 80261FF8 4BDA966D 80263040 4BDA8625 80262DF4 4BDA8871 80261CF8 4BDA996D 80261CF8 4BDA996D 802630B8 4BDA85AD 80262900 4BDA8D65 bl -0x00257A54 /* 8000B664 */
8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 lis r5, 0x8001
8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC lwz r3, [r5 - 0x4944]
8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 mr r30, r31
8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C lha r6, [r30 + 0x032C]
8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 b +0x00000010 /* 8000B670 */
8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 lha r6, [r30 + 0x02B8]
8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 lis r5, 0x8001
8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC stw [r5 - 0x4944], r3
8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 mflr r0
8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 stw [r5 - 0x4940], r0
8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 mr r5, r3
8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 lha r7, [r30 + 0x02B8]
8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 lis r4, 0x8000
8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC ori r4, r4, 0xB6AC
8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 addi r3, r4, 0x0018
8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 crxor crb6, crb6, crb6
8000B690 4838A86D 8000B690 4838D275 8000B690 4838F115 8000B690 4838EEC5 8000B690 4838BB3D 8000B690 4838BB95 8000B690 4838F295 8000B690 4838DD85 bl sprintf /* 8039A924 */
8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 lis r4, 0x8000
8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 ori r4, r4, 0xB6C4
8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 mr r3, r28
8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC lwz r0, [r4 - 0x0004]
8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 mtlr r0
8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 blr
8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A .invalid
8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 bl +0x00503A24 /* 8050F0D4 */
8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 oris r15, r1, 0x2564
8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 .invalid
PSO DC Reticle Colours
DCReticleColors
Binary file not shown.
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
View File
+20 -10
View File
@@ -1449,6 +1449,11 @@ static void server_command_bbchar_savechar(shared_ptr<Client> c, const std::stri
auto l = c->require_lobby();
check_is_game(l, false);
if (is_bb_conversion && is_ep3(c->version())) {
send_text_message(c, "$C6Episode 3 players\ncannot be converted\nto BB format");
return;
}
auto pending_export = make_unique<Client::PendingCharacterExport>();
if (is_bb_conversion) {
@@ -1503,14 +1508,6 @@ static void server_command_savechar(shared_ptr<Client> c, const std::string& arg
}
static void server_command_loadchar(shared_ptr<Client> c, const std::string& args) {
if (!is_v1_or_v2(c->version()) &&
(c->version() != Version::GC_V3) &&
(c->version() != Version::GC_NTE) &&
(c->version() != Version::XB_V3) &&
(c->version() != Version::BB_V4)) {
send_text_message(c, "$C7This command cannot\nbe used on your\ngame version");
return;
}
if (c->login->account->check_flag(Account::Flag::IS_SHARED_ACCOUNT)) {
send_text_message(c, "$C7This command cannot\nbe used on a shared\naccount");
return;
@@ -1523,7 +1520,13 @@ static void server_command_loadchar(shared_ptr<Client> c, const std::string& arg
send_text_message(c, "$C6Player index must\nbe in range 1-16");
return;
}
c->load_backup_character(c->login->account->account_id, index);
shared_ptr<PSOGCEp3CharacterFile::Character> ep3_char;
if (is_ep3(c->version())) {
ep3_char = c->load_ep3_backup_character(c->login->account->account_id, index);
} else {
c->load_backup_character(c->login->account->account_id, index);
}
if (c->version() == Version::BB_V4) {
// On BB, it suffices to simply send the character file again
@@ -1535,10 +1538,12 @@ static void server_command_loadchar(shared_ptr<Client> c, const std::string& arg
} else if ((c->version() == Version::DC_V2) ||
(c->version() == Version::GC_NTE) ||
(c->version() == Version::GC_V3) ||
(c->version() == Version::GC_EP3_NTE) ||
(c->version() == Version::GC_EP3) ||
(c->version() == Version::XB_V3)) {
// TODO: Support extended player info on other versions
auto s = c->require_server_state();
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) ||
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
send_text_message_printf(c, "Can\'t load character\ndata on this game\nversion");
return;
@@ -1582,6 +1587,11 @@ static void server_command_loadchar(shared_ptr<Client> c, const std::string& arg
} else if (c->version() == Version::GC_V3) {
auto gc_char = make_shared<PSOGCCharacterFile::Character>(c->character()->to_gc());
send_set_extended_player_info.operator()<PSOGCCharacterFile::Character>(c, gc_char);
} else if (c->version() == Version::GC_EP3_NTE) {
auto nte_char = make_shared<PSOGCEp3NTECharacter>(*ep3_char);
send_set_extended_player_info.operator()<PSOGCEp3NTECharacter>(c, nte_char);
} else if (c->version() == Version::GC_EP3) {
send_set_extended_player_info.operator()<PSOGCEp3CharacterFile::Character>(c, ep3_char);
} else if (c->version() == Version::XB_V3) {
if (!c->login || !c->login->xb_license) {
throw runtime_error("XB client is not logged in");
+41 -14
View File
@@ -31,6 +31,7 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
if (version == Version::BB_V4) {
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::SAVE_ENABLED);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
return;
}
@@ -41,32 +42,40 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case Version::PC_PATCH:
case Version::BB_PATCH:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case Version::DC_NTE:
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case Version::DC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case Version::PC_NTE:
case Version::PC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case Version::GC_NTE:
case Version::GC_V3:
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case Version::GC_EP3_NTE:
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
break;
case Version::GC_V3:
case Version::GC_EP3:
// Some of these versions have send_function_call and some don't; we
// have to set these flags later when we get sub_version
break;
case Version::XB_V3:
// TODO: Do all versions of XB need this flag? US does, at least.
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
default:
@@ -77,35 +86,38 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case 0x20: // DCNTE, possibly also DCv1 JP
case 0x21: // DCv1 US
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case 0x22: // DCv1 EU 50Hz (presumably)
case 0x23: // DCv1 EU 60Hz (presumably)
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case 0x25: // DCv2 JP
case 0x26: // DCv2 US
case 0x27: // DCv2 EU 50Hz (presumably)
case 0x28: // DCv2 EU 60Hz (presumably)
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x29: // PC
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 Trial Edition, GC Ep1&2 JP v1.2, at least one version of XB
case 0x31: // GC Ep1&2 US v1.0, GC US v1.1, XB US
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case 0x32: // GC Ep1&2 EU 50Hz
case 0x33: // GC Ep1&2 EU 60Hz
case 0x34: // GC Ep1&2 JP v1.3
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case 0x35: // GC Ep1&2 JP v1.4 (Plus)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
@@ -115,24 +127,21 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case 0x36: // GC Ep1&2 US v1.2 (Plus)
case 0x39: // GC Ep1&2 JP v1.5 (Plus)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST);
break;
case 0x40: // GC Ep3 JP and Trial Edition (and BB)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
// sub_version can't be used to tell JP final and Trial Edition apart; we
// instead look at header.flag in the 61 command and set the version then.
break;
case 0x41: // GC Ep3 US (and BB)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x42: // GC Ep3 EU 50Hz
case 0x43: // GC Ep3 EU 60Hz
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST);
break;
default:
throw runtime_error(string_printf("unknown sub_version %" PRIX64, sub_version));
@@ -647,8 +656,9 @@ string Client::character_filename(const std::string& bb_username, int8_t index)
return string_printf("system/players/player_%s_%hhd.psochar", bb_username.c_str(), index);
}
string Client::backup_character_filename(uint32_t account_id, size_t index) {
return string_printf("system/players/backup_player_%" PRIu32 "_%zu.psochar", account_id, index);
string Client::backup_character_filename(uint32_t account_id, size_t index, bool is_ep3) {
return string_printf("system/players/backup_player_%" PRIu32 "_%zu.%s",
account_id, index, is_ep3 ? "pso3char" : "psochar");
}
string Client::character_filename(int8_t index) const {
@@ -954,6 +964,13 @@ void Client::save_character_file(
player_data_log.info("Saved character file %s", filename.c_str());
}
void Client::save_ep3_character_file(
const string& filename,
const PSOGCEp3CharacterFile::Character& character) {
save_file(filename, &character, sizeof(character));
player_data_log.info("Saved Episode 3 character file %s", filename.c_str());
}
void Client::save_character_file() {
if (!this->system_data.get()) {
throw logic_error("no system file loaded");
@@ -984,7 +1001,7 @@ void Client::save_guild_card_file() const {
}
void Client::load_backup_character(uint32_t account_id, size_t index) {
string filename = this->backup_character_filename(account_id, index);
string filename = this->backup_character_filename(account_id, index, false);
auto f = fopen_unique(filename, "rb");
auto header = freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
@@ -1001,6 +1018,16 @@ void Client::load_backup_character(uint32_t account_id, size_t index) {
this->v1_v2_last_reported_disp.reset();
}
shared_ptr<PSOGCEp3CharacterFile::Character> Client::load_ep3_backup_character(uint32_t account_id, size_t index) {
string filename = this->backup_character_filename(account_id, index, true);
auto ch = make_shared<PSOGCEp3CharacterFile::Character>(load_object_file<PSOGCEp3CharacterFile::Character>(filename));
this->character_data = PSOBBCharacterFile::create_from_ep3(*ch);
this->ep3_config = make_shared<Episode3::PlayerConfig>(ch->ep3_config);
this->update_character_data_after_load(this->character_data);
this->v1_v2_last_reported_disp.reset();
return ch;
}
void Client::save_and_unload_character() {
if (this->character_data) {
this->save_character_file();
+10 -4
View File
@@ -35,7 +35,7 @@ public:
// TODO: It'd be nice to use a pattern here (e.g. all server-side flags are
// in the high bits) but that would require re-recording or manually
// rewriting all the tests
CLIENT_SIDE_MASK = 0xFF3CFFFF7C0FFFFB,
CLIENT_SIDE_MASK = 0xFF3CFFFF7C0BFFFB,
// Version-related flags
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
@@ -44,11 +44,12 @@ public:
FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400,
// Flags describing the behavior for send_function_call
NO_SEND_FUNCTION_CALL = 0x0000000000001000,
HAS_SEND_FUNCTION_CALL = 0x0000000000001000,
ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000,
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000,
SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000,
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000,
CAN_RECEIVE_ENABLE_B2_QUEST = 0x0000000000020000,
AWAITING_ENABLE_B2_QUEST = 0x0000000000040000, // Server-side only
// State flags
LOADING = 0x0000000000100000, // Server-side only
@@ -93,6 +94,7 @@ public:
PROXY_BLANK_NAME_ENABLED = 0x0000400000000000,
PROXY_BLOCK_FUNCTION_CALLS = 0x0000800000000000,
PROXY_EP3_UNMASK_WHISPERS = 0x0008000000000000,
PROXY_VIRTUAL_CLIENT = 0x0400000000000000,
// clang-format on
};
enum class ItemDropNotificationMode {
@@ -362,7 +364,7 @@ public:
std::string system_filename() const;
static std::string character_filename(const std::string& bb_username, int8_t index);
static std::string backup_character_filename(uint32_t account_id, size_t index);
static std::string backup_character_filename(uint32_t account_id, size_t index, bool is_ep3);
std::string character_filename(int8_t index = -1) const;
std::string guild_card_filename() const;
std::string shared_bank_filename() const;
@@ -376,11 +378,15 @@ public:
const std::string& filename,
std::shared_ptr<const PSOBBBaseSystemFile> sys,
std::shared_ptr<const PSOBBCharacterFile> character);
static void save_ep3_character_file(
const std::string& filename,
const PSOGCEp3CharacterFile::Character& character);
// Note: This function is not const because it updates the player's play time.
void save_character_file();
void save_guild_card_file() const;
void load_backup_character(uint32_t account_id, size_t index);
std::shared_ptr<PSOGCEp3CharacterFile::Character> load_ep3_backup_character(uint32_t account_id, size_t index);
void save_and_unload_character();
PlayerBank200& current_bank();
+35 -13
View File
@@ -430,8 +430,7 @@ struct C_LegacyLogin_BB_04 {
// Any other nonzero value = Generic failure (101)
// The client config field in this command is ignored by pre-V3 clients as well
// as Episodes 1&2 Trial Edition. All other V3 clients save it as opaque data to
// be returned in a 9E or 9F command later. newserv sends the client config
// anyway to clients that ignore it.
// be returned in a 9E or 9F command later.
// The client will respond with a 96 command, but only the first time it
// receives this command - for later 04 commands, the client will still update
// its client config but will not respond. Changing the security data at any
@@ -718,16 +717,19 @@ check_struct_size(C_MenuSelection_PC_BB_10_Flag03, 0x48);
// Internal name: RcvDownLoad
// Used for downloading online quests. For download quests (to be saved to the
// memory card), use A7 instead.
// This command exists on DC NTE, but it does nothing. DC NTE does not have the
// 44 command, which would also be required for loading quests, so online
// quests canot be loaded on DC NTE.
// All chunks except the last must have 0x400 data bytes. When downloading an
// online quest, the .bin and .dat chunks may be interleaved (although newserv
// currently sends them sequentially). There is a client bug in BB (and
// probably all other versions) where if the quest file's size is a multiple
// of 0x400, the last chunk will have size 0x400, and the client will never
// consider the download complete since it only checks if the last chunk has
// size < 0x400; it does not check if all expected bytes have been received.
// To work around this, newserv appends an extra zero byte if the quest file's
// size is a multiple of 0x400; this byte will be ignored since the PRS
// decompression algorithm contains a stop command, so it will never read it.
// online quest, the .bin and .dat chunks may be interleaved. There is a client
// bug in BB (and probably all other versions) where if the quest file's size
// is a multiple of 0x400, the last chunk will have size 0x400, and the client
// will never consider the download complete since it only checks if the last
// chunk has size < 0x400; it does not check if all expected bytes have been
// received. To work around this, newserv appends an extra zero byte if the
// quest file's size is a multiple of 0x400; this byte will be ignored since
// the PRS decompression algorithm contains a stop command, so it will never
// read it.
// header.flag = file chunk index (start offset / 0x400)
struct S_WriteFile_13_A7 {
@@ -975,6 +977,8 @@ check_struct_size(S_GuildCardSearchResult_BB_41, 0x128);
// Internal name: RcvDownLoadHead
// Used for downloading online quests. The client will react to a 44 command if
// the filename ends in .bin or .dat.
// This command is not implemented on DC NTE, so DC NTE cannot receive online
// quest files.
// For download quests (to be saved to the memory card) and GBA games, the A6
// command is used instead. The client will react to A6 if the filename ends in
// .bin/.dat (quests), .pvr (textures), or .gba (GameBoy Advance games).
@@ -3929,6 +3933,7 @@ struct G_SendGuildCard_BB_6x06 {
} __packed_ws__(G_SendGuildCard_BB_6x06, 0x10C);
// 6x07: Symbol chat
// If UDP mode is enabled, this command is sent via UDP.
struct G_SymbolChat_6x07 {
G_UnusedHeader header;
@@ -4351,6 +4356,7 @@ struct G_SetPosition_6x3F {
} __packed_ws__(G_SetPosition_6x3F, 0x18);
// 6x40: Walk (protected on V3/V4)
// If UDP mode is enabled, this command is sent via UDP.
struct G_WalkToPosition_6x40 {
G_ClientIDHeader header;
@@ -4369,6 +4375,7 @@ struct G_Unknown_6x41 {
} __packed_ws__(G_Unknown_6x41, 0x0C);
// 6x42: Run (protected on V3/V4)
// If UDP mode is enabled, this command is sent via UDP.
struct G_RunToPosition_6x42 {
G_ClientIDHeader header;
@@ -4379,6 +4386,7 @@ struct G_RunToPosition_6x42 {
// 6x43: First attack (protected on V3/V4)
// 6x44: Second attack (protected on V3/V4)
// 6x45: Third attack (protected on V3/V4)
// If UDP mode is enabled, these commands are sent via UDP.
struct G_Attack_6x43_6x44_6x45 {
G_ClientIDHeader header;
@@ -4483,13 +4491,24 @@ struct G_PlayerRevived_6x4F {
} __packed_ws__(G_PlayerRevived_6x4F, 4);
// 6x50: Switch interaction (protected on V3/V4)
// If UDP mode is enabled, this command is sent via UDP.
struct G_SwitchInteraction_6x50 {
G_ClientIDHeader header;
le_uint32_t unknown_a1 = 0;
} __packed_ws__(G_SwitchInteraction_6x50, 8);
// 6x51: Invalid subcommand
// 6x51: Set player angle
// If UDP mode is enabled, this command is sent via UDP.
// This command appears to be vestigial - no version of the game has a handler
// for it (it is always ignored), but PSO GC has a function that sends it. It's
// not known if this function is ever called, or how to trigger it.
struct G_SetPlayerAngle_6x51 {
G_ClientIDHeader header;
le_uint16_t angle = 0;
parray<uint8_t, 2> unused;
} __packed_ws__(G_SetPlayerAngle_6x51, 8);
// 6x52: Set animation state (protected on V3/V4)
@@ -4543,6 +4562,7 @@ struct G_Unknown_6x57 {
} __packed_ws__(G_Unknown_6x57, 4);
// 6x58: Lobby animation (protected on V3/V4)
// If UDP mode is enabled, this command is sent via UDP.
struct G_LobbyAnimation_6x58 {
G_ClientIDHeader header;
@@ -4656,7 +4676,9 @@ struct G_ActivateMagEffect_6x61 {
// 6x62: Unknown
// This command has a handler, but it does nothing even on DC NTE.
// 6x63: Destroy floor item (used when too many items have been dropped)
// 6x63: Destroy floor item
// This is sent by the leader to destroy a floor item when there are 50 or more
// items already on the ground on the current floor.
struct G_DestroyFloorItem_6x5C_6x63 {
G_UnusedHeader header;
+56
View File
@@ -1158,6 +1158,62 @@ void PlayerConfig::encrypt(uint8_t basis) {
this->basis = basis;
}
PlayerConfigNTE::PlayerConfigNTE(const PlayerConfig& config)
: rank_text(config.rank_text),
unknown_a1(config.unknown_a1),
tech_menu_shortcut_entries(config.tech_menu_shortcut_entries),
choice_search_config(config.choice_search_config),
scenario_progress(config.scenario_progress),
unused_offline_records(config.unused_offline_records),
unknown_a4(config.unknown_a4),
is_encrypted(config.is_encrypted),
basis(config.basis),
unused(config.unused),
card_counts(config.card_counts),
card_count_checksums(config.card_count_checksums),
rare_tokens(config.rare_tokens),
decks(config.decks),
unknown_a8(config.unknown_a8),
offline_clv_exp(config.offline_clv_exp),
online_clv_exp(config.online_clv_exp),
recent_human_opponents(config.recent_human_opponents),
unknown_a10(config.unknown_a10),
init_timestamp(config.init_timestamp),
last_online_battle_start_timestamp(config.last_online_battle_start_timestamp),
unknown_t3(config.unknown_t3),
unknown_a14(config.unknown_a14) {
// TODO: Do we need to recompute card_count_checksums? (Here or in operator
// PlayerConfig?)
}
PlayerConfigNTE::operator PlayerConfig() const {
PlayerConfig ret;
ret.rank_text = this->rank_text;
ret.unknown_a1 = this->unknown_a1;
ret.tech_menu_shortcut_entries = this->tech_menu_shortcut_entries;
ret.choice_search_config = this->choice_search_config;
ret.scenario_progress = this->scenario_progress;
ret.unused_offline_records = this->unused_offline_records;
ret.unknown_a4 = this->unknown_a4;
ret.is_encrypted = this->is_encrypted;
ret.basis = this->basis;
ret.unused = this->unused;
ret.card_counts = this->card_counts;
ret.card_count_checksums = this->card_count_checksums;
ret.rare_tokens = this->rare_tokens;
ret.decks = this->decks;
ret.unknown_a8 = this->unknown_a8;
ret.offline_clv_exp = this->offline_clv_exp;
ret.online_clv_exp = this->online_clv_exp;
ret.recent_human_opponents = this->recent_human_opponents;
ret.unknown_a10 = this->unknown_a10;
ret.init_timestamp = this->init_timestamp;
ret.last_online_battle_start_timestamp = this->last_online_battle_start_timestamp;
ret.unknown_t3 = this->unknown_t3;
ret.unknown_a14 = this->unknown_a14;
return ret;
}
Rules::Rules(const JSON& json) {
this->clear();
this->overall_time_limit = json.get_int("overall_time_limit", this->overall_time_limit);
+37 -3
View File
@@ -907,12 +907,12 @@ struct PlayerConfig {
// card counts array is encrypted in memory most of the time, and they went
// out of their way to ensure the game uses an area of memory that almost no
// other game uses, which is also used by the Action Replay.)
/* 05A4:0450 */ parray<be_uint64_t, 0x1C2> rare_tokens;
/* 05A4:0450 */ parray<be_uint64_t, 450> rare_tokens;
/* 13B4:1260 */ parray<uint8_t, 0x80> unknown_a7;
/* 1434:12E0 */ parray<DeckDefinition, 25> decks;
/* 2118:1FC4 */ parray<uint8_t, 0x08> unknown_a8;
/* 2120:1FCC */ be_uint32_t offline_clv_exp; // CLvOff = this / 100
/* 2124:1FD0 */ be_uint32_t online_clv_exp; // CLvOn = this / 100
/* 2120:1FCC */ be_uint32_t offline_clv_exp; // CLvOff = (this / 100) + 1
/* 2124:1FD0 */ be_uint32_t online_clv_exp; // CLvOn = (this / 100) + 1
struct PlayerReference {
/* 00 */ be_uint32_t guild_card_number;
/* 04 */ pstring<TextEncoding::MARKED, 0x18> name;
@@ -941,6 +941,40 @@ struct PlayerConfig {
void encrypt(uint8_t basis);
} __packed_ws__(PlayerConfig, 0x2350);
struct PlayerConfigNTE {
/* 0000 */ pstring<TextEncoding::MARKED, 12> rank_text;
/* 000C */ parray<uint8_t, 0x1C> unknown_a1;
/* 0028 */ parray<be_uint16_t, 20> tech_menu_shortcut_entries;
/* 0050 */ parray<be_uint32_t, 10> choice_search_config;
/* 0078 */ parray<be_uint32_t, 0x10> scenario_progress; // Final has 0x30 entries here
/* 00B8 */ PlayerRecordsBattleBE unused_offline_records;
/* 00D0 */ parray<uint8_t, 4> unknown_a4;
/* 00D4 */ uint8_t is_encrypted;
/* 00D5 */ uint8_t basis;
/* 00D6 */ parray<uint8_t, 2> unused;
/* 00D8 */ parray<uint8_t, 1000> card_counts;
/* 04C0 */ parray<be_uint16_t, 50> card_count_checksums;
/* 0524 */ parray<be_uint64_t, 300> rare_tokens;
/* 0E84 */ parray<DeckDefinition, 25> decks;
/* 1B68 */ parray<uint8_t, 0x08> unknown_a8;
/* 1B70 */ be_uint32_t offline_clv_exp;
/* 1B74 */ be_uint32_t online_clv_exp;
/* 1B78 */ parray<PlayerConfig::PlayerReference, 10> recent_human_opponents;
/* 1C90 */ parray<uint8_t, 0x28> unknown_a10;
/* 1CB8 */ be_uint32_t init_timestamp;
/* 1CBC */ be_uint32_t last_online_battle_start_timestamp;
/* 1CC0 */ be_uint32_t unknown_t3;
/* 1CC4 */ parray<uint8_t, 0x94> unknown_a14;
/* 1D58 */
PlayerConfigNTE() = default;
explicit PlayerConfigNTE(const PlayerConfig& config);
operator PlayerConfig() const;
void decrypt();
void encrypt(uint8_t basis);
} __packed_ws__(PlayerConfigNTE, 0x1D58);
enum class HPType : uint8_t {
DEFEAT_PLAYER = 0,
DEFEAT_TEAM = 1,
+240 -2
View File
@@ -132,8 +132,7 @@ unordered_multimap<string, string> HTTPServer::parse_url_params(const string& qu
}
value.resize(write_offset);
params.emplace(piecewise_construct, forward_as_tuple(it, 0, first_equals),
forward_as_tuple(value));
params.emplace(piecewise_construct, forward_as_tuple(it, 0, first_equals), forward_as_tuple(value));
} else {
params.emplace(it, "");
}
@@ -204,6 +203,233 @@ void HTTPServer::wait_for_stop() {
this->th.join();
}
HTTPServer::WebsocketClient::WebsocketClient(struct evhttp_connection* conn)
: conn(conn),
bev(evhttp_connection_get_bufferevent(this->conn)),
pending_opcode(0xFF),
last_communication_time(now()) {}
HTTPServer::WebsocketClient::~WebsocketClient() {
evhttp_connection_free(this->conn);
}
void HTTPServer::WebsocketClient::reset_pending_frame() {
this->pending_opcode = 0xFF;
this->pending_data.clear();
}
shared_ptr<HTTPServer::WebsocketClient> HTTPServer::enable_websockets(struct evhttp_request* req) {
if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) {
return nullptr;
}
struct evkeyvalq* in_headers = evhttp_request_get_input_headers(req);
const char* connection_header = evhttp_find_header(in_headers, "Connection");
if (!connection_header || strcasecmp(connection_header, "upgrade")) {
return nullptr;
}
const char* upgrade_header = evhttp_find_header(in_headers, "Upgrade");
if (!upgrade_header || strcasecmp(upgrade_header, "websocket")) {
return nullptr;
}
const char* sec_websocket_key_header = evhttp_find_header(in_headers, "Sec-WebSocket-Key");
if (!sec_websocket_key_header) {
return nullptr;
}
// Note: it's important that we make a copy of this header's value since
// we're about to free the original
string sec_websocket_key = sec_websocket_key_header;
string sec_websocket_accept_data = sec_websocket_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
string sec_websocket_accept = base64_encode(sha1(sec_websocket_accept_data));
// Hijack the bufferevent since it's no longer handling HTTP at all
struct evhttp_connection* conn = evhttp_request_get_connection(req);
struct bufferevent* bev = evhttp_connection_get_bufferevent(conn);
bufferevent_setcb(bev, &this->dispatch_on_websocket_read, NULL, &this->dispatch_on_websocket_error, this);
bufferevent_enable(bev, EV_READ | EV_WRITE);
// Send the HTTP reply, which enables websockets
struct evbuffer* out_buf = bufferevent_get_output(bev);
evbuffer_add_printf(out_buf, "HTTP/1.1 101 Switching Protocols\r\n\
Upgrade: websocket\r\n\
Connection: upgrade\r\n\
Sec-WebSocket-Accept: %s\r\n\
\r\n",
sec_websocket_accept.c_str());
return this->bev_to_websocket_client.emplace(bev, new WebsocketClient(conn)).first->second;
}
void HTTPServer::dispatch_on_websocket_read(struct bufferevent* bev, void* ctx) {
reinterpret_cast<HTTPServer*>(ctx)->on_websocket_read(bev);
}
void HTTPServer::dispatch_on_websocket_error(struct bufferevent* bev, short events, void* ctx) {
reinterpret_cast<HTTPServer*>(ctx)->on_websocket_error(bev, events);
}
void HTTPServer::on_websocket_read(struct bufferevent* bev) {
struct evbuffer* in_buf = bufferevent_get_input(bev);
for (;;) {
// We need at most 10 bytes to determine if there's a valid frame, or as
// little as 2
string header_data(10, '\0');
ssize_t bytes_read = evbuffer_copyout(in_buf, const_cast<char*>(header_data.data()), header_data.size());
if (bytes_read < 2) {
break; // Full header not yet available
}
// Get the payload size
bool has_mask = header_data[1] & 0x80;
size_t header_size = 2;
size_t payload_size = header_data[1] & 0x7F;
if (payload_size == 0x7F) {
if (bytes_read < 10) {
break; // Full 64-bit header not yet available
}
payload_size = bswap64(*reinterpret_cast<const uint64_t*>(&header_data[2]));
header_size = 10;
} else if (payload_size == 0x7E) {
if (bytes_read < 4) {
break; // Full 16-bit size header not yet available
}
payload_size = bswap16(*reinterpret_cast<const uint16_t*>(&header_data[2]));
header_size = 4;
}
if (evbuffer_get_length(in_buf) < header_size + payload_size) {
break; // Full message not yet available
}
// Full message is available; skip the header bytes (we already read them)
// and read the masking key if needed
evbuffer_drain(in_buf, header_size);
uint8_t mask_key[4];
if (has_mask) {
evbuffer_remove(in_buf, mask_key, 4);
}
shared_ptr<WebsocketClient> c = this->bev_to_websocket_client.at(bev);
c->last_communication_time = now();
// Read and unmask message data
string payload(payload_size, '\0');
evbuffer_remove(in_buf, const_cast<char*>(payload.data()), payload_size);
if (has_mask) {
for (size_t x = 0; x < payload_size; x++) {
payload[x] ^= mask_key[x & 3];
}
}
// If the current message is a control message, respond appropriately
// (these can be sent in the middle of fragmented messages)
uint8_t opcode = header_data[0] & 0x0F;
if (opcode & 0x08) {
if (opcode == 0x0A) {
// Ping response; ignore it
} else if (opcode == 0x08) {
// Close message
this->send_websocket_message(bev, payload, 0x08);
this->disconnect_websocket_client(bev);
} else if (opcode == 0x09) {
// Ping message
this->send_websocket_message(bev, payload, 0x0A);
} else {
// Unknown control message type
this->disconnect_websocket_client(bev);
}
break;
}
// If there's an existing pending message, the current message's opcode
// should be zero; if there's no pending message, it must not be zero
if ((c->pending_opcode != 0xFF) == (opcode != 0)) {
this->disconnect_websocket_client(bev);
break;
}
// At this point, we have read a full message; we must not break out of
// this loop in case there are further messages available.
// Save the message opcode, if present, and append the frame data
if (opcode) {
c->pending_opcode = opcode;
}
c->pending_data += payload;
// If the FIN bit is set, then the frame is complete - append the payload
// to any pending payloads and call the message handler. If the FIN bit
// isn't set, we need to receive at least one continuation frame to
// complete the message.
if (header_data[0] & 0x80) {
this->handle_websocket_message(c, c->pending_opcode, c->pending_data);
c->reset_pending_frame();
}
}
}
void HTTPServer::on_websocket_error(struct bufferevent* bev, short events) {
if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
this->disconnect_websocket_client(bev);
}
}
void HTTPServer::disconnect_websocket_client(struct bufferevent* bev) {
auto it = this->bev_to_websocket_client.find(bev);
this->handle_websocket_disconnect(it->second);
this->bev_to_websocket_client.erase(it);
}
void HTTPServer::send_websocket_message(struct bufferevent* bev,
const string& message, uint8_t opcode) {
string header;
header.push_back(0x80 | (opcode & 0x0F));
if (message.size() > 65535) {
header.push_back(0x7F);
header.resize(10);
*reinterpret_cast<uint64_t*>(const_cast<char*>(header.data() + 2)) = bswap64(message.size());
} else if (message.size() > 0x7D) {
header.push_back(0x7E);
header.resize(4);
*reinterpret_cast<uint16_t*>(const_cast<char*>(header.data() + 2)) = bswap16(message.size());
} else {
header.push_back(message.size());
}
struct evbuffer* out_buf = bufferevent_get_output(bev);
evbuffer_add(out_buf, header.data(), header.size());
evbuffer_add(out_buf, message.data(), message.size());
}
void HTTPServer::send_websocket_message(shared_ptr<WebsocketClient> c, const string& message, uint8_t opcode) {
this->send_websocket_message(c->bev, message, opcode);
}
void HTTPServer::handle_websocket_message(shared_ptr<WebsocketClient>, uint8_t, const string&) {
// Currently we just ignore any messages from the client
}
void HTTPServer::handle_websocket_disconnect(shared_ptr<WebsocketClient> c) {
this->rare_drop_subscribers.erase(c);
}
void HTTPServer::send_rare_drop_notification(shared_ptr<const JSON> message) {
forward_to_event_thread(this->base, [this, message]() -> void {
if (this->rare_drop_subscribers.empty()) {
return;
}
string serialized = message->serialize();
for (const auto& c : this->rare_drop_subscribers) {
this->send_websocket_message(c, serialized);
}
});
}
void HTTPServer::dispatch_handle_request(struct evhttp_request* req, void* ctx) {
reinterpret_cast<HTTPServer*>(ctx)->handle_request(req);
}
@@ -960,11 +1186,23 @@ void HTTPServer::handle_request(struct evhttp_request* req) {
"/y/proxy-clients",
"/y/lobbies",
"/y/server",
"/y/rare-drops/stream",
"/y/summary",
"/y/all",
});
ret = make_shared<JSON>(JSON::dict({{"endpoints", std::move(endpoints_json)}}));
} else if (uri == "/y/rare-drops/stream") {
auto c = this->enable_websockets(req);
if (!c) {
throw http_error(400, "this path requires a websocket connection");
} else {
this->rare_drop_subscribers.emplace(c);
auto version_message = JSON::dict({{"ServerType", "newserv"}});
this->send_websocket_message(c, version_message.serialize());
return;
}
} else if (uri == "/y/data/ep3-cards") {
ret = make_shared<JSON>(this->generate_ep3_cards_json(false));
} else if (uri == "/y/data/ep3-cards-trial") {
+38
View File
@@ -28,6 +28,8 @@ public:
void schedule_stop();
void wait_for_stop();
void send_rare_drop_notification(std::shared_ptr<const JSON> message);
protected:
class http_error : public std::runtime_error {
public:
@@ -35,11 +37,47 @@ protected:
int code;
};
struct WebsocketClient {
struct evhttp_connection* conn;
struct bufferevent* bev;
uint8_t pending_opcode;
std::string pending_data;
uint64_t last_communication_time;
void* context;
WebsocketClient(struct evhttp_connection* conn);
~WebsocketClient();
void reset_pending_frame();
};
std::shared_ptr<ServerState> state;
std::shared_ptr<struct event_base> base;
std::shared_ptr<struct evhttp> http;
std::thread th;
std::unordered_set<std::shared_ptr<WebsocketClient>> rare_drop_subscribers;
std::unordered_map<struct bufferevent*, std::shared_ptr<WebsocketClient>> bev_to_websocket_client;
std::shared_ptr<WebsocketClient> enable_websockets(struct evhttp_request* req);
static void dispatch_on_websocket_read(struct bufferevent* bev, void* ctx);
static void dispatch_on_websocket_error(struct bufferevent* bev, short events, void* ctx);
void on_websocket_read(struct bufferevent* bev);
void on_websocket_error(struct bufferevent* bev, short events);
void disconnect_websocket_client(struct bufferevent* bev);
void send_websocket_message(struct bufferevent* bev, const std::string& message, uint8_t opcode = 0x01);
void send_websocket_message(std::shared_ptr<WebsocketClient> c, const std::string& message, uint8_t opcode = 0x01);
virtual void handle_websocket_message(std::shared_ptr<WebsocketClient> c, uint8_t opcode, const std::string& message);
virtual void handle_websocket_disconnect(std::shared_ptr<WebsocketClient> c);
void thread_fn();
static void dispatch_handle_request(struct evhttp_request* req, void* ctx);
+9 -9
View File
@@ -340,16 +340,16 @@ unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string
// Check for binary operators at the root level
using BinType = BinaryOperatorNode::Type;
static const vector<vector<pair<std::string, BinaryOperatorNode::Type>>> binary_operator_levels = {
{{make_pair("*", BinType::MULTIPLY)}, {make_pair("/", BinType::DIVIDE)}, {make_pair("%", BinType::MODULUS)}},
{{make_pair("+", BinType::ADD)}, {make_pair("-", BinType::SUBTRACT)}},
{{make_pair("<<", BinType::LEFT_SHIFT)}, {make_pair(">>", BinType::RIGHT_SHIFT)}},
{{make_pair("<=", BinType::LESS_OR_EQUAL)}, {make_pair(">=", BinType::GREATER_OR_EQUAL)}, {make_pair("<", BinType::LESS_THAN)}, {make_pair(">", BinType::GREATER_THAN)}},
{{make_pair("==", BinType::EQUAL)}, {make_pair("!=", BinType::NOT_EQUAL)}},
{{make_pair("&", BinType::BITWISE_AND)}},
{{make_pair("^", BinType::BITWISE_XOR)}},
{{make_pair("|", BinType::BITWISE_OR)}},
{{make_pair("&&", BinType::LOGICAL_AND)}},
{{make_pair("||", BinType::LOGICAL_OR)}},
{{make_pair("&&", BinType::LOGICAL_AND)}},
{{make_pair("|", BinType::BITWISE_OR)}},
{{make_pair("^", BinType::BITWISE_XOR)}},
{{make_pair("&", BinType::BITWISE_AND)}},
{{make_pair("==", BinType::EQUAL)}, {make_pair("!=", BinType::NOT_EQUAL)}},
{{make_pair("<=", BinType::LESS_OR_EQUAL)}, {make_pair(">=", BinType::GREATER_OR_EQUAL)}, {make_pair("<", BinType::LESS_THAN)}, {make_pair(">", BinType::GREATER_THAN)}},
{{make_pair("<<", BinType::LEFT_SHIFT)}, {make_pair(">>", BinType::RIGHT_SHIFT)}},
{{make_pair("+", BinType::ADD)}, {make_pair("-", BinType::SUBTRACT)}},
{{make_pair("*", BinType::MULTIPLY)}, {make_pair("/", BinType::DIVIDE)}, {make_pair("%", BinType::MODULUS)}},
};
for (const auto& operators : binary_operator_levels) {
size_t paren_level = 0;
+9 -14
View File
@@ -12,7 +12,8 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
// On PC (and presumably DC), the client sends a 6x29 after this to delete the
// used item. On GC and later versions, this does not happen, so we should
// delete the item here.
bool is_v3_or_later = is_v3(c->version()) || is_v4(c->version());
bool is_v4 = ::is_v4(c->version());
bool is_v3_or_later = is_v3(c->version()) || is_v4;
bool should_delete_item = is_v3_or_later;
auto player = c->character();
@@ -36,20 +37,14 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
}
auto& weapon = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::WEAPON)];
// Don't enforce the weapon's grind limit on V1 and V2. This is necessary
// because the V2 client replaces its inventory items on the fly with items
// compatible with V1 when sending the 61 and 98 commands. There appears to
// be no way to disable this behavior, so there's no way for the server to
// get an accurate picture of what's actually in the player's inventory, so
// there's no way to know if we would be enforcing the correct grind limit.
if (is_v3_or_later) {
auto item_parameter_table = s->item_parameter_table(c->version());
auto weapon_def = item_parameter_table->get_weapon(weapon.data.data1[1], weapon.data.data1[2]);
if (weapon.data.data1[3] >= weapon_def.max_grind) {
throw runtime_error("weapon already at maximum grind");
}
// Only enforce grind limits on BB, since the server doesn't have direct
// control over players' inventories on other versions
auto item_parameter_table = s->item_parameter_table(c->version());
auto weapon_def = item_parameter_table->get_weapon(weapon.data.data1[1], weapon.data.data1[2]);
if (is_v4 && (weapon.data.data1[3] >= weapon_def.max_grind)) {
throw runtime_error("weapon already at maximum grind");
}
weapon.data.data1[3] += (item.data.data1[2] + 1);
weapon.data.data1[3] = min<uint8_t>(weapon.data.data1[3] + item.data.data1[2] + 1, weapon_def.max_grind);
} else if ((primary_identifier & 0xFFFF0000) == 0x030B0000) { // Material
auto p = c->character();
+1 -2
View File
@@ -91,8 +91,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
SERVER_DUPLICATE = 4,
};
std::weak_ptr<ServerState>
server_state;
std::weak_ptr<ServerState> server_state;
PrefixedLogger log;
uint32_t lobby_id;
+13 -8
View File
@@ -1237,7 +1237,13 @@ Action a_assemble_quest_script(
uncompressed .bind file instead.\n",
+[](Arguments& args) {
string text = read_input_data(args);
string result = assemble_quest_script(text);
const string& input_filename = args.get<string>(1, false);
string include_dir = (!input_filename.empty() && (input_filename != "-"))
? dirname(input_filename)
: ".";
string result = assemble_quest_script(text, include_dir);
bool compress = !args.get<bool>("decompressed");
if (compress) {
result = prs_compress_optimal(result);
@@ -2543,7 +2549,6 @@ Action a_run_server_replay_log(
shared_ptr<ServerShell> shell;
shared_ptr<ReplaySession> replay_session;
shared_ptr<HTTPServer> http_server;
if (is_replay) {
config_log.info("Starting proxy server");
state->proxy_server = make_shared<ProxyServer>(base, state);
@@ -2653,10 +2658,10 @@ Action a_run_server_replay_log(
if (!state->http_addresses.empty() || !state->http_addresses.empty()) {
config_log.info("Starting HTTP server");
http_server = make_shared<HTTPServer>(state);
state->http_server = make_shared<HTTPServer>(state);
for (const auto& it : state->http_addresses) {
auto netloc = parse_netloc(it);
http_server->listen(netloc.first, netloc.second);
state->http_server->listen(netloc.first, netloc.second);
}
}
}
@@ -2700,8 +2705,8 @@ Action a_run_server_replay_log(
if (state->bb_patch_server) {
state->bb_patch_server->schedule_stop();
}
if (http_server) {
http_server->schedule_stop();
if (state->http_server) {
state->http_server->schedule_stop();
}
if (state->pc_patch_server) {
config_log.info("Waiting for PC_V2 patch server to stop");
@@ -2711,9 +2716,9 @@ Action a_run_server_replay_log(
config_log.info("Waiting for BB_V4 patch server to stop");
state->bb_patch_server->wait_for_stop();
}
if (http_server) {
if (state->http_server) {
config_log.info("Waiting for HTTP server to stop");
http_server->wait_for_stop();
state->http_server->wait_for_stop();
}
state->proxy_server.reset(); // Break reference cycle
});
+8 -7
View File
@@ -80,13 +80,14 @@ constexpr uint32_t SWITCH_ASSIST = 0xAA0707AA;
constexpr uint32_t BLOCK_EVENTS = 0xAA0808AA;
constexpr uint32_t BLOCK_PATCHES = 0xAA0909AA;
constexpr uint32_t SAVE_FILES = 0xAA0A0AAA;
constexpr uint32_t RED_NAME = 0xAA0B0BAA;
constexpr uint32_t BLANK_NAME = 0xAA0C0CAA;
constexpr uint32_t SUPPRESS_LOGIN = 0xAA0D0DAA;
constexpr uint32_t SKIP_CARD = 0xAA0E0EAA;
constexpr uint32_t EP3_INFINITE_MESETA = 0xAA0F0FAA;
constexpr uint32_t EP3_INFINITE_TIME = 0xAA1010AA;
constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA1111AA;
constexpr uint32_t VIRTUAL_CLIENT = 0xAA0B0BAA;
constexpr uint32_t RED_NAME = 0xAA0C0CAA;
constexpr uint32_t BLANK_NAME = 0xAA0D0DAA;
constexpr uint32_t SUPPRESS_LOGIN = 0xAA0E0EAA;
constexpr uint32_t SKIP_CARD = 0xAA0F0FAA;
constexpr uint32_t EP3_INFINITE_MESETA = 0xAA1010AA;
constexpr uint32_t EP3_INFINITE_TIME = 0xAA1111AA;
constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA1212AA;
} // namespace ProxyOptionsMenuItemID
namespace TeamRewardMenuItemID {
+10 -8
View File
@@ -169,7 +169,7 @@ static HandlerResult S_G_9A(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t
}
cmd.unused1 = 0;
cmd.unused2 = 0;
cmd.sub_version = ses->sub_version;
cmd.sub_version = ses->effective_sub_version();
cmd.is_extended = (ses->remote_guild_card_number < 0) ? 1 : 0;
cmd.language = ses->language();
cmd.serial_number.encode(string_printf("%08" PRIX32 "", ses->login->account->account_id));
@@ -275,7 +275,7 @@ static HandlerResult S_V123P_02_17(
}
cmd.unknown_a1 = 0;
cmd.unknown_a2 = 0;
cmd.sub_version = ses->sub_version;
cmd.sub_version = ses->effective_sub_version();
cmd.is_extended = 0;
cmd.language = ses->language();
cmd.serial_number.encode(string_printf("%08" PRIX32 "", ses->login->account->account_id));
@@ -313,7 +313,7 @@ static HandlerResult S_V123P_02_17(
cmd.player_tag = 0x00010000;
cmd.guild_card_number = ses->remote_guild_card_number;
}
cmd.sub_version = ses->sub_version;
cmd.sub_version = ses->effective_sub_version();
cmd.serial_number.encode(string_printf("%08" PRIX32 "", ses->login->account->account_id));
cmd.access_key.encode(*access_key);
if (ses->version() != Version::GC_NTE) {
@@ -337,7 +337,7 @@ static HandlerResult S_V123P_02_17(
}
cmd.unused1 = 0;
cmd.unused2 = 0;
cmd.sub_version = ses->sub_version;
cmd.sub_version = ses->effective_sub_version();
cmd.is_extended = 0;
cmd.language = ses->language();
cmd.serial_number.encode(string_printf("%08" PRIX32 "", ses->login->account->account_id));
@@ -367,7 +367,7 @@ static HandlerResult S_V123P_02_17(
C_VerifyAccount_V3_DB cmd;
cmd.serial_number.encode(string_printf("%08" PRIX32 "", ses->login->account->account_id));
cmd.access_key.encode(ses->login->gc_license->access_key);
cmd.sub_version = ses->sub_version;
cmd.sub_version = ses->effective_sub_version();
cmd.serial_number2 = cmd.serial_number;
cmd.access_key2 = cmd.access_key;
cmd.password.encode(ses->login->gc_license->password);
@@ -396,7 +396,7 @@ static HandlerResult S_V123P_02_17(
cmd.guild_card_number = guild_card_number;
cmd.unused1 = 0;
cmd.unused2 = 0;
cmd.sub_version = ses->sub_version;
cmd.sub_version = ses->effective_sub_version();
cmd.is_extended = 0;
cmd.language = ses->language();
cmd.serial_number.encode(string_printf("%08" PRIX32, fake_serial_number));
@@ -432,7 +432,7 @@ static HandlerResult S_V123P_02_17(
}
cmd.unused1 = 0;
cmd.unused2 = 0;
cmd.sub_version = ses->sub_version;
cmd.sub_version = ses->effective_sub_version();
cmd.is_extended = (ses->remote_guild_card_number < 0) ? 1 : 0;
cmd.language = ses->language();
cmd.serial_number.encode(ses->login->xb_license->gamertag);
@@ -1145,7 +1145,9 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
} else if (data[0] == 0x5F) {
const auto& cmd = check_size_t<G_DropItem_DC_6x5F>(data, sizeof(G_DropItem_PC_V3_BB_6x5F));
send_item_notification_if_needed(ses->require_server_state(), ses->client_channel, ses->config, cmd.item.item, true);
ItemData item = cmd.item.item;
item.decode_for_version(ses->version());
send_item_notification_if_needed(ses->require_server_state(), ses->client_channel, ses->config, item, true);
} else if ((data[0] == 0x60) || (static_cast<uint8_t>(data[0]) == 0xA2)) {
return SC_6x60_6xA2(ses, data);
+5
View File
@@ -160,6 +160,11 @@ public:
inline uint8_t language() const {
return this->client_channel.language;
}
inline uint32_t effective_sub_version() const {
return this->config.check_flag(Client::Flag::PROXY_VIRTUAL_CLIENT)
? default_sub_version_for_version(this->version())
: this->sub_version;
}
void set_version(Version v);
void resume(
+8 -4
View File
@@ -461,17 +461,22 @@ bool Quest::has_version_any_language(Version v) const {
return ((it != this->versions.end()) && ((it->first & 0xFF00) == k));
}
shared_ptr<const VersionedQuest> Quest::version(Version v, uint8_t language) const {
shared_ptr<const VersionedQuest> Quest::version(Version v, uint8_t language, bool allow_language_fallback) const {
// Return the requested version, if it exists
try {
return this->versions.at(this->versions_key(v, language));
} catch (const out_of_range&) {
}
if (!allow_language_fallback) {
return nullptr;
}
// Return the English version, if it exists
try {
return this->versions.at(this->versions_key(v, 1));
} catch (const out_of_range&) {
}
// Return the first language, if it exists
auto it = this->versions.lower_bound(this->versions_key(v, 0));
if ((it == this->versions.end()) || ((it->first & 0xFF00) != this->versions_key(v, 0))) {
@@ -543,7 +548,8 @@ QuestIndex::QuestIndex(
file_data = decode_dlq_data(load_file(file_path));
filename.resize(filename.size() - 4);
} else if (ends_with(filename, ".txt")) {
file_data = assemble_quest_script(load_file(file_path));
string include_dir = dirname(file_path);
file_data = assemble_quest_script(load_file(file_path), include_dir);
filename.resize(filename.size() - 4);
if (ends_with(filename, ".bin")) {
filename.push_back('d');
@@ -587,8 +593,6 @@ QuestIndex::QuestIndex(
throw runtime_error("qst file contains unsupported file type: " + it.first);
}
}
} else {
static_game_data_log.warning("(%s) Skipping file (unsupported format)", filename.c_str());
}
} catch (const exception& e) {
+1 -1
View File
@@ -123,7 +123,7 @@ public:
void add_version(std::shared_ptr<const VersionedQuest> vq);
bool has_version(Version v, uint8_t language) const;
bool has_version_any_language(Version v) const;
std::shared_ptr<const VersionedQuest> version(Version v, uint8_t language) const;
std::shared_ptr<const VersionedQuest> version(Version v, uint8_t language, bool allow_language_fallback = true) const;
static uint32_t versions_key(Version v, uint8_t language);
+129 -87
View File
@@ -12,6 +12,12 @@
#include <unordered_map>
#include <vector>
#ifdef HAVE_RESOURCE_FILE
#include <resource_file/Emulators/PPC32Emulator.hh>
#include <resource_file/Emulators/SH4Emulator.hh>
#include <resource_file/Emulators/X86Emulator.hh>
#endif
#include "BattleParamsIndex.hh"
#include "CommandFormats.hh"
#include "Compression.hh"
@@ -280,7 +286,7 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
{0x000A, "letb", {REG, INT8}, F_V3_V4}, // Sets register to a fixed value (int8)
{0x000B, "letw", {REG, INT16}, F_V3_V4}, // Sets register to a fixed value (int16)
{0x000C, "leta", {REG, REG}, F_V3_V4}, // Sets regA to the memory address of regB
{0x000D, "leto", {REG, SCRIPT16}, F_V3_V4}, // Sets register to the offset (NOT memory address) of a function
{0x000D, "leto", {REG, SCRIPT16}, F_V3_V4}, // Sets register to the address of an entry in the quest function table
{0x0010, "set", {REG}, F_V0_V4}, // Sets a register to 1
{0x0011, "clear", {REG}, F_V0_V4}, // Sets a register to 0
{0x0012, "rev", {REG}, F_V0_V4}, // Sets a register to 0 if it's nonzero and vice versa
@@ -830,6 +836,7 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
{0xF94A, "olga_flow_is_dead", {REG}, F_V3_V4},
{0xF94B, "particle_effect_nc", {{REG_SET_FIXED, 4}}, F_V3_V4},
{0xF94C, "player_effect_nc", {{REG_SET_FIXED, 4}}, F_V3_V4},
{0xF94D, "has_ep3_save_file", {REG}, F_GC_V3 | F_ARGS}, // (PSO Plus only) Returns 1 if a file named PSO3_CHARACTER is present on either memory card
{0xF94D, "give_or_take_card", {{REG_SET_FIXED, 2}}, F_GC_EP3}, // regsA[0] is card_id; card is given if regsA[1] >= 0, otherwise it's taken
{0xF94D, "unknown_F94D", {INT32, REG}, F_XB_V3 | F_ARGS}, // Related to voice chat. argA is a client ID; a value is read from that player's TVoiceChatClient object and (!!value) is placed in regB. This value is set by the 6xB3 command; TODO: figure out what that value represents and name this opcode appropriately
{0xF94D, "nop_F94D", {}, F_V4},
@@ -1133,10 +1140,14 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
arg_stack_values.emplace_back(ArgStackValue::Type::LABEL, label_id);
}
if (label_id >= function_table.size()) {
dasm_arg = string_printf("label%04" PRIX32 " /* invalid */", label_id);
dasm_arg = string_printf("label%04" PRIX32, label_id);
} else {
auto& l = function_table.at(label_id);
dasm_arg = string_printf("label%04" PRIX32 " /* %04" PRIX32 " */", label_id, l->offset);
if (reassembly_mode) {
dasm_arg = string_printf("label%04" PRIX32, label_id);
} else {
dasm_arg = string_printf("label%04" PRIX32 " /* %04" PRIX32 " */", label_id, l->offset);
}
l->references.emplace(opcode_start_offset);
l->add_data_type(arg.data_type);
if (arg.data_type == Arg::DataType::SCRIPT) {
@@ -1154,10 +1165,14 @@ std::string disassemble_quest_script(const void* data, size_t size, Version vers
dasm_arg += (dasm_arg.empty() ? "[" : ", ");
uint32_t label_id = cmd_r.get_u16l();
if (label_id >= function_table.size()) {
dasm_arg += string_printf("function%04" PRIX32 " /* invalid */", label_id);
dasm_arg += string_printf("label%04" PRIX32, label_id);
} else {
auto& l = function_table.at(label_id);
dasm_arg += string_printf("label%04" PRIX32 " /* %04" PRIX32 " */", label_id, l->offset);
if (reassembly_mode) {
dasm_arg += string_printf("label%04" PRIX32, label_id);
} else {
dasm_arg += string_printf("label%04" PRIX32 " /* %04" PRIX32 " */", label_id, l->offset);
}
l->references.emplace(opcode_start_offset);
l->add_data_type(arg.data_type);
if (arg.data_type == Arg::DataType::SCRIPT) {
@@ -1660,100 +1675,104 @@ Episode find_quest_episode_from_script(const void* data, size_t size, Version ve
throw logic_error("invalid quest version");
}
const auto& opcodes = opcodes_for_version(version);
unordered_set<Episode> found_episodes;
// The set_episode opcode should always be in the first function (0)
StringReader cmd_r = r.sub(code_offset + r.pget_u32l(function_table_offset));
try {
const auto& opcodes = opcodes_for_version(version);
// The set_episode opcode should always be in the first function (0)
StringReader cmd_r = r.sub(code_offset + r.pget_u32l(function_table_offset));
while (!cmd_r.eof()) {
uint16_t opcode = cmd_r.get_u8();
if ((opcode & 0xFE) == 0xF8) {
opcode = (opcode << 8) | cmd_r.get_u8();
}
while (!cmd_r.eof()) {
uint16_t opcode = cmd_r.get_u8();
if ((opcode & 0xFE) == 0xF8) {
opcode = (opcode << 8) | cmd_r.get_u8();
}
const QuestScriptOpcodeDefinition* def = nullptr;
try {
def = opcodes.at(opcode);
} catch (const out_of_range&) {
}
const QuestScriptOpcodeDefinition* def = nullptr;
try {
def = opcodes.at(opcode);
} catch (const out_of_range&) {
}
if (def == nullptr) {
throw runtime_error(string_printf("unknown quest opcode %04hX", opcode));
}
if (def == nullptr) {
throw runtime_error(string_printf("unknown quest opcode %04hX", opcode));
}
if (def->flags & F_RET) {
break;
}
if (def->flags & F_RET) {
break;
}
if (!(def->flags & F_ARGS)) {
for (const auto& arg : def->args) {
using Type = QuestScriptOpcodeDefinition::Argument::Type;
string dasm_arg;
switch (arg.type) {
case Type::LABEL16:
cmd_r.skip(2);
break;
case Type::LABEL32:
cmd_r.skip(4);
break;
case Type::LABEL16_SET:
if (def->flags & F_PASS) {
throw logic_error("LABEL16_SET cannot be pushed to arg stack");
}
cmd_r.skip(cmd_r.get_u8() * 2);
break;
case Type::REG:
cmd_r.skip(1);
break;
case Type::REG_SET:
if (def->flags & F_PASS) {
throw logic_error("REG_SET cannot be pushed to arg stack");
}
cmd_r.skip(cmd_r.get_u8());
break;
case Type::REG_SET_FIXED:
if (def->flags & F_PASS) {
throw logic_error("REG_SET_FIXED cannot be pushed to arg stack");
}
cmd_r.skip(1);
break;
case Type::REG32_SET_FIXED:
if (def->flags & F_PASS) {
throw logic_error("REG32_SET_FIXED cannot be pushed to arg stack");
}
cmd_r.skip(4);
break;
case Type::INT8:
cmd_r.skip(1);
break;
case Type::INT16:
cmd_r.skip(2);
break;
case Type::INT32:
if (def->flags & F_SET_EPISODE) {
found_episodes.emplace(episode_for_quest_episode_number(cmd_r.get_u32l()));
} else {
if (!(def->flags & F_ARGS)) {
for (const auto& arg : def->args) {
using Type = QuestScriptOpcodeDefinition::Argument::Type;
string dasm_arg;
switch (arg.type) {
case Type::LABEL16:
cmd_r.skip(2);
break;
case Type::LABEL32:
cmd_r.skip(4);
}
break;
case Type::FLOAT32:
cmd_r.skip(4);
break;
case Type::CSTRING:
if (use_wstrs) {
for (uint16_t ch = cmd_r.get_u16l(); ch; ch = cmd_r.get_u16l()) {
break;
case Type::LABEL16_SET:
if (def->flags & F_PASS) {
throw logic_error("LABEL16_SET cannot be pushed to arg stack");
}
} else {
for (uint8_t ch = cmd_r.get_u8(); ch; ch = cmd_r.get_u8()) {
cmd_r.skip(cmd_r.get_u8() * 2);
break;
case Type::REG:
cmd_r.skip(1);
break;
case Type::REG_SET:
if (def->flags & F_PASS) {
throw logic_error("REG_SET cannot be pushed to arg stack");
}
}
break;
default:
throw logic_error("invalid argument type");
cmd_r.skip(cmd_r.get_u8());
break;
case Type::REG_SET_FIXED:
if (def->flags & F_PASS) {
throw logic_error("REG_SET_FIXED cannot be pushed to arg stack");
}
cmd_r.skip(1);
break;
case Type::REG32_SET_FIXED:
if (def->flags & F_PASS) {
throw logic_error("REG32_SET_FIXED cannot be pushed to arg stack");
}
cmd_r.skip(4);
break;
case Type::INT8:
cmd_r.skip(1);
break;
case Type::INT16:
cmd_r.skip(2);
break;
case Type::INT32:
if (def->flags & F_SET_EPISODE) {
found_episodes.emplace(episode_for_quest_episode_number(cmd_r.get_u32l()));
} else {
cmd_r.skip(4);
}
break;
case Type::FLOAT32:
cmd_r.skip(4);
break;
case Type::CSTRING:
if (use_wstrs) {
for (uint16_t ch = cmd_r.get_u16l(); ch; ch = cmd_r.get_u16l()) {
}
} else {
for (uint8_t ch = cmd_r.get_u8(); ch; ch = cmd_r.get_u8()) {
}
}
break;
default:
throw logic_error("invalid argument type");
}
}
}
}
} catch (const exception& e) {
log_warning("Cannot determine episode from quest script (%s)", e.what());
}
if (found_episodes.size() > 1) {
@@ -1963,7 +1982,7 @@ struct RegisterAssigner {
array<shared_ptr<Register>, 0x100> numbered_regs;
};
std::string assemble_quest_script(const std::string& text) {
std::string assemble_quest_script(const std::string& text, const std::string& include_directory) {
auto lines = split(text, '\n');
// Strip comments and whitespace
@@ -2195,6 +2214,29 @@ std::string assemble_quest_script(const std::string& text) {
} else if (starts_with(line, ".zero ")) {
size_t size = stoull(line.substr(6), nullptr, 0);
code_w.extend_by(size, 0x00);
} else if (starts_with(line, ".include_bin ")) {
string filename = line.substr(13);
strip_whitespace(filename);
code_w.write(load_file(include_directory + "/" + filename));
} else if (starts_with(line, ".include_native ")) {
#ifdef HAVE_RESOURCE_FILE
string filename = line.substr(16);
strip_whitespace(filename);
string native_text = load_file(include_directory + "/" + filename);
string code;
if (is_ppc(quest_version)) {
code = std::move(PPC32Emulator::assemble(native_text).code);
} else if (is_x86(quest_version)) {
code = std::move(X86Emulator::assemble(native_text).code);
} else if (is_sh4(quest_version)) {
code = std::move(SH4Emulator::assemble(native_text).code);
} else {
throw runtime_error("unknown architecture");
}
code_w.write(code);
#else
throw runtime_error("native code cannot be compiled; rebuild newserv with libresource_file");
#endif
}
continue;
}
+1 -1
View File
@@ -83,6 +83,6 @@ struct PSOQuestHeaderBB {
Episode episode_for_quest_episode_number(uint8_t episode_number);
std::string disassemble_quest_script(const void* data, size_t size, Version version, uint8_t override_language = 0xFF, bool reassembly_mode = false);
std::string assemble_quest_script(const std::string& text);
std::string assemble_quest_script(const std::string& text, const std::string& include_directory);
Episode find_quest_episode_from_script(const void* data, size_t size, Version version);
+114 -54
View File
@@ -103,6 +103,8 @@ static shared_ptr<const Menu> proxy_options_menu_for_client(shared_ptr<const Cli
"Save files", "Save local copies of\nfiles from the\nremote server\n(quests, etc.)");
}
if (s->proxy_enable_login_options) {
add_flag_option(ProxyOptionsMenuItemID::VIRTUAL_CLIENT, Client::Flag::PROXY_VIRTUAL_CLIENT,
"Virtual client", "");
add_flag_option(ProxyOptionsMenuItemID::RED_NAME, Client::Flag::PROXY_RED_NAME_ENABLED,
"Red name", "Set the colors\nof your name and\nChallenge Mode\nrank to red");
add_flag_option(ProxyOptionsMenuItemID::BLANK_NAME, Client::Flag::PROXY_BLANK_NAME_ENABLED,
@@ -128,7 +130,7 @@ void send_first_pre_lobby_commands(shared_ptr<Client> c, std::function<void()> o
if (function_compiler_available() &&
!c->config.check_flag(Client::Flag::HAS_AUTO_PATCHES) &&
!c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
prepare_client_for_patches(c, [wc = weak_ptr<Client>(c), on_complete = std::move(on_complete)]() -> void {
auto c = wc.lock();
if (!c) {
@@ -226,6 +228,10 @@ void send_client_to_proxy_server(shared_ptr<Client> c) {
if (ses->config.check_flag(Client::Flag::PROXY_ZERO_REMOTE_GUILD_CARD)) {
ses->remote_guild_card_number = 0;
}
if (c->version() == Version::GC_EP3) {
send_ep3_media_update(c, 4, 0, "");
ses->config.clear_flag(Client::Flag::HAS_EP3_MEDIA_UPDATES);
}
send_reconnect(c, s->connect_address_for_client(c), local_port);
});
@@ -236,20 +242,6 @@ static void send_proxy_destinations_menu(shared_ptr<Client> c) {
send_menu(c, s->proxy_destinations_menu(c->version()));
}
static bool send_enable_send_function_call_if_applicable(shared_ptr<Client> c) {
auto s = c->require_server_state();
if (function_compiler_available() && c->config.check_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) {
if (s->ep3_send_function_call_enabled) {
send_quest_buffer_overflow(c);
} else {
c->config.set_flag(Client::Flag::NO_SEND_FUNCTION_CALL);
}
c->config.clear_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void on_connect(std::shared_ptr<Client> c) {
@@ -363,6 +355,29 @@ static void send_main_menu(shared_ptr<Client> c) {
send_menu(c, main_menu);
}
void on_login_server_login_complete(shared_ptr<Client> c) {
auto s = c->require_server_state();
if (s->pre_lobby_event && (!is_ep3(c->version()) || s->ep3_menu_song < 0)) {
send_change_event(c, s->pre_lobby_event);
}
if (is_ep3(c->version())) {
send_ep3_rank_update(c);
send_get_player_info(c);
}
if (s->welcome_message.empty() ||
c->config.check_flag(Client::Flag::NO_D6) ||
!c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) {
c->config.clear_flag(Client::Flag::AT_WELCOME_MESSAGE);
send_update_client_config(c, false);
send_main_menu(c);
} else {
send_message_box(c, s->welcome_message.c_str());
}
}
void on_login_complete(shared_ptr<Client> c) {
c->convert_account_to_temporary_if_nte();
@@ -372,24 +387,49 @@ void on_login_complete(shared_ptr<Client> c) {
case ServerBehavior::LOGIN_SERVER: {
auto s = c->require_server_state();
if (s->pre_lobby_event && (!is_ep3(c->version()) || s->ep3_menu_song < 0)) {
send_change_event(c, s->pre_lobby_event);
if (c->config.check_flag(Client::Flag::CAN_RECEIVE_ENABLE_B2_QUEST) &&
!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) &&
(s->enable_send_function_call_quest_num >= 0)) {
auto q = s->default_quest_index->get(s->enable_send_function_call_quest_num);
if (q) {
uint8_t q_language;
switch (c->sub_version) {
case 0x39:
q_language = 0; // Japanese (JP Plus v1.5)
break;
case 0x42:
case 0x43:
q_language = 2; // German (EU Ep3)
break;
case 0x41:
q_language = 4; // Spanish (US Ep3)
break;
case 0x36:
case 0x3A:
default:
q_language = 1; // English (US Plus v1.2 + customizations)
}
auto vq = q->version(is_ep3(c->version()) ? Version::GC_V3 : c->version(), q_language, false);
if (vq) {
c->config.set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
c->config.set_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST);
send_update_client_config(c, false);
c->log.info("Sending %c version of quest \"%s\"", char_for_language_code(vq->language), vq->name.c_str());
string bin_filename = vq->bin_filename();
string dat_filename = vq->dat_filename();
string xb_filename = vq->xb_filename();
send_open_quest_file(c, bin_filename, bin_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->bin_contents);
send_open_quest_file(c, dat_filename, dat_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->dat_contents);
send_command(c, 0xAC, 0x00);
}
}
}
if (is_ep3(c->version())) {
send_ep3_rank_update(c);
send_get_player_info(c);
}
if (s->welcome_message.empty() ||
c->config.check_flag(Client::Flag::NO_D6) ||
!c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) {
c->config.clear_flag(Client::Flag::AT_WELCOME_MESSAGE);
send_enable_send_function_call_if_applicable(c);
send_update_client_config(c, false);
send_main_menu(c);
} else {
send_message_box(c, s->welcome_message.c_str());
if (!c->config.check_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST)) {
on_login_server_login_complete(c);
}
break;
}
@@ -876,18 +916,6 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
c->channel.language = base_cmd->language;
set_console_client_flags(c, base_cmd->sub_version);
// See system/client-functions/Episode3USAQuestBufferOverflow.ppc.s for where
// this value gets set. We use this to determine if the client has already run
// the code or not; sending it again when the client has already run it will
// likely cause the client to crash.
if (base_cmd->unused1 == 0x5F5CA297) {
c->config.clear_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL);
c->config.clear_flag(Client::Flag::NO_SEND_FUNCTION_CALL);
} else if (!s->ep3_send_function_call_enabled && c->config.check_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) {
c->config.clear_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL);
c->config.set_flag(Client::Flag::NO_SEND_FUNCTION_CALL);
}
try {
switch (c->version()) {
case Version::DC_V2: {
@@ -1743,7 +1771,6 @@ static void on_D6_V3(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_menu(c, s->information_menu(c->version()));
} else if (c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) {
c->config.clear_flag(Client::Flag::AT_WELCOME_MESSAGE);
send_enable_send_function_call_if_applicable(c);
send_update_client_config(c, false);
send_main_menu(c);
}
@@ -2195,7 +2222,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
if (!function_compiler_available()) {
throw runtime_error("function compiler not available");
}
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
prepare_client_for_patches(c, [c]() -> void {
@@ -2207,7 +2234,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
if (!function_compiler_available()) {
throw runtime_error("function compiler not available");
}
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
// We have to prepare the client for patches here, even though we
@@ -2222,7 +2249,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
if (!function_compiler_available()) {
throw runtime_error("function compiler not available");
}
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
prepare_client_for_patches(c, [c]() -> void {
@@ -2350,6 +2377,9 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
case ProxyOptionsMenuItemID::SAVE_FILES:
c->config.toggle_flag(Client::Flag::PROXY_SAVE_FILES);
goto resend_proxy_options_menu;
case ProxyOptionsMenuItemID::VIRTUAL_CLIENT:
c->config.toggle_flag(Client::Flag::PROXY_VIRTUAL_CLIENT);
goto resend_proxy_options_menu;
case ProxyOptionsMenuItemID::RED_NAME:
c->config.toggle_flag(Client::Flag::PROXY_RED_NAME_ENABLED);
goto resend_proxy_options_menu;
@@ -2577,7 +2607,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_main_menu(c);
} else {
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
@@ -2595,7 +2625,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_main_menu(c);
} else {
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
@@ -2615,7 +2645,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_main_menu(c);
} else {
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
@@ -2700,6 +2730,13 @@ static void on_84(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
const auto& cmd = check_size_t<C_LobbySelection_84>(data);
auto s = c->require_server_state();
if ((c->server_behavior == ServerBehavior::LOGIN_SERVER) &&
c->config.check_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST)) {
on_login_server_login_complete(c);
c->config.clear_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST);
return;
}
if (cmd.menu_id != MenuID::LOBBY) {
send_message_box(c, "Incorrect menu ID");
return;
@@ -3206,6 +3243,12 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
c->update_channel_name();
// If the player is BB and has just left a game, sync their save file to the
// client to make sure it's up to date
if ((c->version() == Version::BB_V4) && (command == 0x98)) {
send_complete_player_bb(c);
}
if (command == 0x61) {
if (c->pending_character_export) {
unique_ptr<Client::PendingCharacterExport> pending_export = std::move(c->pending_character_export);
@@ -3217,7 +3260,7 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
pending_export->dest_bb_license->username, pending_export->character_index);
} else {
filename = Client::backup_character_filename(
pending_export->dest_account->account_id, pending_export->character_index);
pending_export->dest_account->account_id, pending_export->character_index, is_ep3(c->version()));
}
if (s->player_files_manager->get_character(filename)) {
@@ -3290,7 +3333,8 @@ static void on_30(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
} else {
filename = Client::backup_character_filename(
pending_export->dest_account->account_id,
pending_export->character_index);
pending_export->character_index,
is_ep3(c->version()));
}
auto s = c->require_server_state();
@@ -3299,6 +3343,21 @@ static void on_30(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
return;
}
if (is_ep3(c->version())) {
try {
if (c->version() == Version::GC_EP3_NTE) {
PSOGCEp3CharacterFile::Character ch(check_size_t<PSOGCEp3NTECharacter>(data));
Client::save_ep3_character_file(filename, ch);
} else {
Client::save_ep3_character_file(filename, check_size_t<PSOGCEp3CharacterFile::Character>(data));
}
send_text_message(c, "$C7Character data saved\n(full save file)");
} catch (const exception& e) {
send_text_message_printf(c, "$C6Character data could\nnot be saved:\n%s", e.what());
}
return;
}
shared_ptr<PSOBBCharacterFile> bb_char;
switch (c->version()) {
case Version::DC_V2:
@@ -3313,13 +3372,14 @@ static void on_30(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
case Version::XB_V3:
bb_char = PSOBBCharacterFile::create_from_xb(check_size_t<PSOXBCharacterFileCharacter>(data));
break;
case Version::GC_EP3_NTE:
case Version::GC_EP3:
throw logic_error("Episode 3 case not handled correctly");
case Version::DC_NTE:
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
case Version::PC_NTE:
case Version::PC_V2:
case Version::GC_EP3_NTE:
case Version::GC_EP3:
case Version::BB_V4:
default:
throw logic_error("extended player data command not implemented for version");
+96 -28
View File
@@ -10,6 +10,7 @@
#include "Client.hh"
#include "Compression.hh"
#include "HTTPServer.hh"
#include "Items.hh"
#include "Lobby.hh"
#include "Loggers.hh"
@@ -1958,7 +1959,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
ItemData item = cmd.item.item;
item.decode_for_version(c->version());
l->on_item_id_generated_externally(item.id);
l->add_item(cmd.item.floor, item, cmd.item.x, cmd.item.z, 0x100F);
l->add_item(cmd.item.floor, item, cmd.item.x, cmd.item.z, (l->drop_mode == Lobby::DropMode::CLIENT) ? 0x100F : 0x000F);
auto name = s->describe_item(c->version(), item, false);
l->log.info("Player %hhu (leader) created floor item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
@@ -1983,7 +1984,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
send_command_t(lc, command, flag, cmd);
}
}
send_item_notification_if_needed(s, lc->channel, lc->config, cmd.item.item, true);
send_item_notification_if_needed(s, lc->channel, lc->config, item, true);
}
}
@@ -2072,9 +2073,26 @@ static void on_pick_up_item_generic(
if (should_send_game_notif || should_send_global_notif) {
string p_name = p->disp.name.decode();
string desc = s->describe_item(c->version(), fi->data, true);
string message = string_printf("$C6%s$C7 found\n%s", p_name.c_str(), desc.c_str());
string bb_message = string_printf("$C6%s$C7 has found %s", p_name.c_str(), desc.c_str());
string desc_ingame = s->describe_item(c->version(), fi->data, true);
string desc_http = s->describe_item(c->version(), fi->data, false);
if (s->http_server) {
auto message = make_shared<JSON>(JSON::dict({
{"PlayerAccountID", c->login->account->account_id},
{"PlayerName", p_name},
{"PlayerVersion", name_for_enum(c->version())},
{"GameName", l->name},
{"GameDropMode", name_for_enum(l->drop_mode)},
{"ItemData", fi->data.hex()},
{"ItemDescription", desc_http},
{"NotifyGame", should_send_game_notif},
{"NotifyServer", should_send_global_notif},
}));
s->http_server->send_rare_drop_notification(message);
}
string message = string_printf("$C6%s$C7 found\n%s", p_name.c_str(), desc_ingame.c_str());
string bb_message = string_printf("$C6%s$C7 has found %s", p_name.c_str(), desc_ingame.c_str());
if (should_send_global_notif) {
for (auto& it : s->channel_to_client) {
if (it.second->login &&
@@ -3535,8 +3553,35 @@ void on_item_reward_request_bb(shared_ptr<Client> c, uint8_t, uint8_t, void* dat
item = cmd.item_data;
item.enforce_min_stack_size(limits);
item.id = l->generate_item_id(c->lobby_client_id);
c->character()->add_item(item, limits);
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
// The logic for the item_create and item_create2 opcodes (B3 and B4)
// includes a precondition check to see if the player can actually add the
// item to their inventory or not, and the entire command is skipped if not.
// However, on BB, the implementation performs this check and sends a 6xCA
// command instead - the item is not immediately added to the inventory, and
// is instead added when the server sends back a 6xBE command. So if a quest
// creates multiple items in quick succession, there may be another 6xCA/6xBE
// sequence in flight, and the client's check if an item can be created may
// pass when a 6xBE command that would make it fail is already on the way
// from the server. To handle this, we simply ignore any 6xCA command if the
// item can't be created.
try {
c->character()->add_item(item, limits);
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
if (l->log.should_log(LogLevel::INFO)) {
auto name = s->describe_item(c->version(), item, false);
l->log.info("Player %hu created inventory item %08" PRIX32 " (%s) via quest command",
c->lobby_client_id, item.id.load(), name.c_str());
c->print_inventory(stderr);
}
} catch (const out_of_range&) {
if (l->log.should_log(LogLevel::INFO)) {
auto name = s->describe_item(c->version(), item, false);
l->log.info("Player %hu attempted to create inventory item %08" PRIX32 " (%s) via quest command, but it cannot be placed in their inventory",
c->lobby_client_id, item.id.load(), name.c_str());
}
}
}
void on_transfer_item_via_mail_message_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
@@ -3679,29 +3724,52 @@ static void on_destroy_floor_item(shared_ptr<Client> c, uint8_t command, uint8_t
}
auto s = c->require_server_state();
auto fi = l->remove_item(cmd.floor, cmd.item_id, 0xFF);
auto name = s->describe_item(c->version(), fi->data, false);
l->log.info("Player %hhu destroyed floor item %08" PRIX32 " (%s)", c->lobby_client_id, cmd.item_id.load(), name.c_str());
shared_ptr<Lobby::FloorItem> fi;
try {
fi = l->remove_item(cmd.floor, cmd.item_id, 0xFF);
} catch (const out_of_range&) {
}
// Only forward to players for whom the item was visible
for (size_t z = 0; z < l->clients.size(); z++) {
auto lc = l->clients[z];
if (lc && fi->visible_to_client(z)) {
if (lc->version() != c->version()) {
G_DestroyFloorItem_6x5C_6x63 out_cmd = cmd;
switch (lc->version()) {
case Version::DC_NTE:
out_cmd.header.subcommand = is_6x5C ? 0x4E : 0x55;
break;
case Version::DC_V1_11_2000_PROTOTYPE:
out_cmd.header.subcommand = is_6x5C ? 0x55 : 0x5C;
break;
default:
out_cmd.header.subcommand = is_6x5C ? 0x5C : 0x63;
if (!fi) {
// There are generally two data races that could occur here. Either the
// player attempted to evict the item at the same time the server did (that
// is, the client's and server's 6x63 commands crossed paths on the
// network), or the player attempted to evict an item that was already
// picked up. The former case is easy to handle; we can just ignore the
// command. The latter case is more difficult - we have to know which
// player picked up the item and send a 6x2B command to the sender, to sync
// their item state with the server's again. We can't just look through the
// players' inventories to find the item ID, since item IDs can be
// destroyed when stackable items or Meseta are picked up.
// TODO: We don't actually handle the evict/pickup conflict case. This case
// is probably quite rare, but we should eventually handle it.
l->log.info("Player %hhu attempted to destroy floor item %08" PRIX32 ", but it is missing",
c->lobby_client_id, cmd.item_id.load());
} else {
auto name = s->describe_item(c->version(), fi->data, false);
l->log.info("Player %hhu destroyed floor item %08" PRIX32 " (%s)", c->lobby_client_id, cmd.item_id.load(), name.c_str());
// Only forward to players for whom the item was visible
for (size_t z = 0; z < l->clients.size(); z++) {
auto lc = l->clients[z];
if (lc && fi->visible_to_client(z)) {
if (lc->version() != c->version()) {
G_DestroyFloorItem_6x5C_6x63 out_cmd = cmd;
switch (lc->version()) {
case Version::DC_NTE:
out_cmd.header.subcommand = is_6x5C ? 0x4E : 0x55;
break;
case Version::DC_V1_11_2000_PROTOTYPE:
out_cmd.header.subcommand = is_6x5C ? 0x55 : 0x5C;
break;
default:
out_cmd.header.subcommand = is_6x5C ? 0x5C : 0x63;
}
send_command_t(lc, command, flag, out_cmd);
} else {
send_command_t(lc, command, flag, cmd);
}
send_command_t(lc, command, flag, out_cmd);
} else {
send_command_t(lc, command, flag, cmd);
}
}
}
+101
View File
@@ -257,6 +257,65 @@ Image PSOGCSnapshotFile::decode_image() const {
return ret;
}
PSOGCEp3CharacterFile::Character::Character(const PSOGCEp3NTECharacter& nte)
: inventory(nte.inventory),
disp(nte.disp),
flags(nte.flags),
creation_timestamp(nte.creation_timestamp),
signature(nte.signature),
play_time_seconds(nte.play_time_seconds),
option_flags(nte.option_flags),
save_count(nte.save_count),
ppp_username(nte.ppp_username),
ppp_password(nte.ppp_password),
seq_vars(nte.seq_vars),
death_count(nte.death_count),
bank(nte.bank),
guild_card(nte.guild_card),
symbol_chats(nte.symbol_chats),
chat_shortcuts(nte.chat_shortcuts),
auto_reply(nte.auto_reply),
info_board(nte.info_board),
battle_records(nte.battle_records),
unknown_a10(nte.unknown_a10),
challenge_record_stats(nte.challenge_record_stats),
ep3_config(nte.ep3_config),
unknown_a11(nte.unknown_a11),
unknown_a12(nte.unknown_a12),
unknown_a13(nte.unknown_a13) {
this->ep3_config.backup_visual = this->disp.visual;
}
PSOGCEp3CharacterFile::Character::operator PSOGCEp3NTECharacter() const {
PSOGCEp3NTECharacter ret;
ret.inventory = this->inventory;
ret.disp = this->disp;
ret.flags = this->flags;
ret.creation_timestamp = this->creation_timestamp;
ret.signature = this->signature;
ret.play_time_seconds = this->play_time_seconds;
ret.option_flags = this->option_flags;
ret.save_count = this->save_count;
ret.ppp_username = this->ppp_username;
ret.ppp_password = this->ppp_password;
ret.seq_vars = this->seq_vars;
ret.death_count = this->death_count;
ret.bank = this->bank;
ret.guild_card = this->guild_card;
ret.symbol_chats = this->symbol_chats;
ret.chat_shortcuts = this->chat_shortcuts;
ret.auto_reply = this->auto_reply;
ret.info_board = this->info_board;
ret.battle_records = this->battle_records;
ret.unknown_a10 = this->unknown_a10;
ret.challenge_record_stats = this->challenge_record_stats;
ret.ep3_config = Episode3::PlayerConfigNTE(this->ep3_config);
ret.unknown_a11 = this->unknown_a11;
ret.unknown_a12 = this->unknown_a12;
ret.unknown_a13 = this->unknown_a13;
return ret;
}
void PSOBBGuildCardFile::Entry::clear() {
this->data.clear();
this->unknown_a1.clear(0);
@@ -598,6 +657,48 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_gc(const PSOGCCha
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_ep3(const PSOGCEp3CharacterFile::Character& ep3) {
auto ret = make_shared<PSOBBCharacterFile>();
ret->inventory = ep3.inventory;
uint8_t language = ret->inventory.language;
ret->disp = ep3.disp.to_bb(language, language);
ret->creation_timestamp = ep3.creation_timestamp.load();
ret->play_time_seconds = ep3.play_time_seconds.load();
ret->option_flags = ep3.option_flags.load();
ret->save_count = ep3.save_count.load();
ret->death_count = ep3.death_count.load();
ret->bank = ep3.bank;
ret->guild_card = ep3.guild_card;
for (size_t z = 0; z < std::min<size_t>(ret->symbol_chats.size(), ep3.symbol_chats.size()); z++) {
auto& ret_sc = ret->symbol_chats[z];
const auto& gc_sc = ep3.symbol_chats[z];
ret_sc.present = gc_sc.present.load();
ret_sc.name.encode(gc_sc.name.decode(language), language);
ret_sc.spec = gc_sc.spec;
}
for (size_t z = 0; z < std::min<size_t>(ret->shortcuts.size(), ep3.chat_shortcuts.size()); z++) {
ret->shortcuts[z] = ep3.chat_shortcuts[z].convert<false, TextEncoding::UTF16, 0x50>(language);
}
ret->auto_reply.encode(ep3.auto_reply.decode(language), language);
ret->info_board.encode(ep3.info_board.decode(language), language);
ret->battle_records = ep3.battle_records;
ret->unknown_a4 = ep3.ep3_config.unknown_a4;
ret->challenge_records.rank_title.encode(ep3.ep3_config.rank_text.decode(language), language);
for (size_t z = 0; z < std::min<size_t>(ret->tech_menu_shortcut_entries.size(), ep3.ep3_config.tech_menu_shortcut_entries.size()); z++) {
ret->tech_menu_shortcut_entries[z] = ep3.ep3_config.tech_menu_shortcut_entries[z].load();
}
ret->choice_search_config.disabled = !!(ret->option_flags & 0x00040000);
for (size_t z = 0; z < 5; z++) {
ret->choice_search_config.entries[z].parent_choice_id = ep3.ep3_config.choice_search_config[z * 2].load();
ret->choice_search_config.entries[z].choice_id = ep3.ep3_config.choice_search_config[z * 2 + 1].load();
}
for (size_t z = 0; z < std::min<size_t>(ret->quest_counters.size(), ep3.ep3_config.scenario_progress.size()); z++) {
ret->quest_counters[z] = ep3.ep3_config.scenario_progress[z].load();
}
ret->offline_battle_records = ep3.ep3_config.unused_offline_records;
return ret;
}
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_xb(const PSOXBCharacterFileCharacter& xb) {
auto ret = make_shared<PSOBBCharacterFile>();
ret->inventory = xb.inventory;
+44 -3
View File
@@ -531,6 +531,44 @@ struct PSOGCCharacterFile {
/* 1156C */
} __packed_ws__(PSOGCCharacterFile, 0x1156C);
struct PSOGCEp3NTECharacter {
// This structure is internally split into two by the game. The offsets here
// are relative to the start of this structure (first column), and relative
// to the start of the second internal structure (second column).
/* 0000:---- */ PlayerInventoryBE inventory;
/* 034C:---- */ PlayerDispDataDCPCV3BE disp;
/* 041C:0000 */ be_uint32_t flags = 0;
/* 0420:0004 */ be_uint32_t creation_timestamp = 0;
/* 0424:0008 */ be_uint32_t signature = 0xA205B064;
/* 0428:000C */ be_uint32_t play_time_seconds = 0;
/* 042C:0010 */ be_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ be_uint32_t save_count = 1;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
// seq_vars is an array of 8192 bits, which contain all the Episode 3 quest
// progress flags. This includes things like which maps are unlocked, which
// NPC decks are unlocked, and whether the player has a VIP card or not.
/* 0460:0044 */ parray<uint8_t, 0x400> seq_vars;
/* 0860:0444 */ be_uint32_t death_count = 0;
/* 0864:0448 */ PlayerBank200BE bank;
/* 1B2C:1710 */ GuildCardGCBE guild_card;
/* 1BBC:17A0 */ parray<SaveFileSymbolChatEntryGC, 12> symbol_chats;
/* 1FDC:1BC0 */ parray<SaveFileShortcutEntryGC, 20> chat_shortcuts;
/* 266C:2250 */ pstring<TextEncoding::MARKED, 0xAC> auto_reply;
/* 2718:22FC */ pstring<TextEncoding::MARKED, 0xAC> info_board;
// // In this struct, place_counts[0] is win_count and [1] is loss_count
/* 27C4:23A8 */ PlayerRecordsBattleBE battle_records;
/* 27DC:23C0 */ parray<uint8_t, 4> unknown_a10;
/* 27E0:23C4 */ PlayerRecordsChallengeV3BE::Stats challenge_record_stats;
/* 28B8:249C */ Episode3::PlayerConfigNTE ep3_config;
/* 4610:41F4 */ be_uint32_t unknown_a11 = 0;
/* 4614:41F8 */ be_uint32_t unknown_a12 = 0;
/* 4618:41FC */ be_uint32_t unknown_a13 = 0;
/* 461C:4200 */
PSOGCEp3NTECharacter() = default;
} __packed_ws__(PSOGCEp3NTECharacter, 0x461C);
struct PSOGCEp3CharacterFile {
/* 00000 */ be_uint32_t checksum = 0; // crc32 of this field (as 0) through end of struct
struct Character {
@@ -541,10 +579,8 @@ struct PSOGCEp3CharacterFile {
/* 034C:---- */ PlayerDispDataDCPCV3BE disp;
/* 041C:0000 */ be_uint32_t flags = 0;
/* 0420:0004 */ be_uint32_t creation_timestamp = 0;
/* 0424:0008 */ be_uint32_t signature = 0xA204B064;
/* 0424:0008 */ be_uint32_t signature = 0xA205B064;
/* 0428:000C */ be_uint32_t play_time_seconds = 0;
// See the comment in PSOGCCharacterFile::Character about what the bits in
// this field mean.
/* 042C:0010 */ be_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ be_uint32_t save_count = 1;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
@@ -572,6 +608,10 @@ struct PSOGCEp3CharacterFile {
/* 39AC:3590 */ be_uint32_t unknown_a12 = 0;
/* 39B0:3594 */ be_uint32_t unknown_a13 = 0;
/* 39B4:3598 */
Character() = default;
explicit Character(const PSOGCEp3NTECharacter& nte);
operator PSOGCEp3NTECharacter() const;
} __packed_ws__(Character, 0x39B4);
/* 00004 */ parray<Character, 7> characters;
/* 193F0 */ pstring<TextEncoding::ASCII, 0x10> serial_number; // As %08X (not decimal)
@@ -691,6 +731,7 @@ struct PSOBBCharacterFile {
static std::shared_ptr<PSOBBCharacterFile> create_from_dc_v2(const PSODCV2CharacterFile& dc);
static std::shared_ptr<PSOBBCharacterFile> create_from_gc_nte(const PSOGCNTECharacterFileCharacter& gc_nte);
static std::shared_ptr<PSOBBCharacterFile> create_from_gc(const PSOGCCharacterFile::Character& gc);
static std::shared_ptr<PSOBBCharacterFile> create_from_ep3(const PSOGCEp3CharacterFile::Character& ep3);
static std::shared_ptr<PSOBBCharacterFile> create_from_xb(const PSOXBCharacterFileCharacter& xb);
PSODCV2CharacterFile to_dc_v2() const;
+6 -7
View File
@@ -474,7 +474,7 @@ void send_function_call(
uint32_t checksum_addr,
uint32_t checksum_size,
uint32_t override_relocations_offset) {
if (client_config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!client_config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw logic_error("client does not support function calls");
}
if (code.get() && client_config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
@@ -511,7 +511,7 @@ bool send_protected_command(std::shared_ptr<Client> c, const void* data, size_t
case Version::BB_V4: {
auto s = c->require_server_state();
if (!s->enable_v3_v4_protected_subcommands ||
c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) ||
!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
return false;
}
@@ -1429,7 +1429,7 @@ void send_menu_t(shared_ptr<Client> c, shared_ptr<const Menu> menu, bool is_info
is_visible &= !c->config.check_flag(Client::Flag::NO_D6);
}
if (item.flags & MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL) {
is_visible &= !c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL);
is_visible &= c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
}
if (item.flags & MenuItem::Flag::REQUIRES_SAVE_DISABLED) {
is_visible &= !c->config.check_flag(Client::Flag::SAVE_ENABLED);
@@ -2385,21 +2385,20 @@ void send_self_leave_notification(shared_ptr<Client> c) {
}
void send_get_player_info(shared_ptr<Client> c, bool request_extended) {
// TODO: Support extended player info on Ep3 (JP and NTE)
switch (c->version()) {
case Version::DC_NTE:
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
case Version::PC_NTE:
case Version::PC_V2:
case Version::GC_EP3_NTE:
case Version::GC_EP3:
case Version::BB_V4:
request_extended = false;
break;
case Version::DC_V2:
case Version::GC_NTE:
case Version::GC_V3:
case Version::GC_EP3_NTE:
case Version::GC_EP3:
case Version::XB_V3:
break;
default:
@@ -2407,7 +2406,7 @@ void send_get_player_info(shared_ptr<Client> c, bool request_extended) {
}
if (request_extended &&
!c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) &&
c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) &&
!c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
auto s = c->require_server_state();
prepare_client_for_patches(c, [wc = weak_ptr<Client>(c)]() {
+15 -1
View File
@@ -82,7 +82,21 @@ void ServerShell::thread_fn() {
for (;;) {
fprintf(stdout, "newserv> ");
fflush(stdout);
string command = fgets(stdin);
string command;
uint64_t read_start_usecs = now();
try {
command = fgets(stdin);
} catch (const io_error& e) {
// Cygwin sometimes causes fgets() to fail with errno -1 when the
// terminal window is resized. We ignore these events unless the read
// failed immediately (which probably means it would fail again if we
// retried immediately).
if (now() - read_start_usecs < 1000000 || e.error != -1) {
throw;
}
log_warning("I/O error reading from terminal: %s (%d)", e.what(), e.error);
continue;
}
// If command is empty (not even a \n), it's probably EOF
if (command.empty()) {
+1 -1
View File
@@ -793,7 +793,7 @@ void ServerState::load_config_early() {
this->default_rare_notifs_enabled_v3_v4 = this->default_rare_notifs_enabled_v1_v2;
this->default_rare_notifs_enabled_v1_v2 = this->config_json->get_bool("RareNotificationsEnabledByDefaultV1V2", this->default_rare_notifs_enabled_v1_v2);
this->default_rare_notifs_enabled_v3_v4 = this->config_json->get_bool("RareNotificationsEnabledByDefaultV3V4", this->default_rare_notifs_enabled_v3_v4);
this->ep3_send_function_call_enabled = this->config_json->get_bool("EnableEpisode3SendFunctionCall", false);
this->enable_send_function_call_quest_num = this->config_json->get_int("EnableSendFunctionCallQuestNumber", -1);
this->enable_v3_v4_protected_subcommands = this->config_json->get_bool("EnableV3V4ProtectedSubcommands", false);
this->catch_handler_exceptions = this->config_json->get_bool("CatchHandlerExceptions", true);
+3 -1
View File
@@ -36,6 +36,7 @@
class ProxyServer;
class Server;
class IPStackSimulator;
class HTTPServer;
struct PortConfiguration {
std::string name;
@@ -119,7 +120,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v4;
std::unordered_map<std::string, std::pair<uint8_t, uint32_t>> quest_counter_fields; // For $qfread command
uint64_t persistent_game_idle_timeout_usecs = 0;
bool ep3_send_function_call_enabled = false;
int64_t enable_send_function_call_quest_num = -1;
bool enable_v3_v4_protected_subcommands = false;
bool catch_handler_exceptions = true;
bool ep3_infinite_meseta = false;
@@ -270,6 +271,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<Server> game_server;
std::shared_ptr<PatchServer> pc_patch_server;
std::shared_ptr<PatchServer> bb_patch_server;
std::shared_ptr<HTTPServer> http_server;
explicit ServerState(const std::string& config_filename = "");
ServerState(std::shared_ptr<struct event_base> base, const std::string& config_filename, bool is_replay);
+29
View File
@@ -199,6 +199,34 @@ ServerBehavior enum_for_name<ServerBehavior>(const char* name) {
}
}
uint32_t default_sub_version_for_version(Version version) {
switch (version) {
case Version::DC_NTE:
return 0x20;
case Version::DC_V1_11_2000_PROTOTYPE:
return 0x21;
case Version::DC_V1:
return 0x21;
case Version::DC_V2:
return 0x26;
case Version::PC_NTE:
return 0x28;
case Version::PC_V2:
return 0x29;
case Version::GC_NTE:
case Version::GC_V3:
case Version::XB_V3:
return 0x30;
case Version::GC_EP3_NTE:
case Version::GC_EP3:
return 0x40;
case Version::BB_V4:
return 0x41;
default:
return 0x00;
}
}
uint32_t default_specific_version_for_version(Version version, int64_t sub_version) {
// For versions that don't support send_function_call by default, we need
// to set the specific_version based on sub_version. Fortunately, all
@@ -224,6 +252,7 @@ uint32_t default_specific_version_for_version(Version version, int64_t sub_versi
case 0x33: // GC Ep1&2 EU 60Hz
return SPECIFIC_VERSION_GC_V3_EU; // 3OP0
case 0x36: // GC Ep1&2 US v1.2 (Plus)
case 0x3A: // GC Ep1&2 US v1.2 (Plus) GMK edition
return SPECIFIC_VERSION_GC_V3_US_12; // 3OE2
case 0x34: // GC Ep1&2 JP v1.3
return SPECIFIC_VERSION_GC_V3_JP_13; // 3OJ3
+1
View File
@@ -161,6 +161,7 @@ constexpr uint32_t SPECIFIC_VERSION_XB_V3_INDETERMINATE = 0x344F0000; // 4O__
constexpr uint32_t SPECIFIC_VERSION_BB_V4_INDETERMINATE = 0x35000000; // 5___
constexpr uint32_t SPECIFIC_VERSION_INDETERMINATE = 0x00000000; // ____
uint32_t default_sub_version_for_version(Version version);
uint32_t default_specific_version_for_version(Version version, int64_t sub_version);
bool specific_version_is_indeterminate(uint32_t specific_version);
bool specific_version_is_dc(uint32_t specific_version);
@@ -0,0 +1,30 @@
ret
# Call table: 2 functions (on_window_created, on_hp_updated)
jmp on_window_created
on_hp_updated:
call rewrite_string
movsx ecx, word [ebp + 0x02BC] # Replaced opcode at callsite
ret
on_window_created:
mov [0x00010C08], eax # prev_desc
push ebp
mov ebp, ebx
call rewrite_string
pop ebp
mov dword [esp + 4], 0x00010C1C # Change first argument to desc_buf
jmp [0x00010C04] # Call original function
rewrite_string:
movsx eax, word [ebp + 0x02BC] # max HP
push eax
movsx eax, word [ebp + 0x0330] # current HP
push eax
push dword [0x00010C08] # prev_desc
push 0x00010C0C # desc_template
push 0x00010C1C # desc_buf
call [0x00010C00] # sprintf
add esp, 0x14
ret
@@ -9,30 +9,72 @@ reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
# region @ 8000B650 (108 bytes)
.data 0x8000B650 # address
.data 0x0000006C # size
.data 0x3CA08001 # 8000B650 => lis r5, 0x8001
.data 0x8065B6BC # 8000B654 => lwz r3, [r5 - 0x4944]
.data 0x7FFEFB78 # 8000B658 => mr r30, r31
.data 0xA8DE032C # 8000B65C => lha r6, [r30 + 0x032C]
.data 0x48000010 # 8000B660 => b +0x00000010 /* 8000B670 */
.data 0xA8DE02B8 # 8000B664 => lha r6, [r30 + 0x02B8]
.data 0x3CA08001 # 8000B668 => lis r5, 0x8001
.data 0x9065B6BC # 8000B66C => stw [r5 - 0x4944], r3
.data 0x7C0802A6 # 8000B670 => mflr r0
.data 0x9005B6C0 # 8000B674 => stw [r5 - 0x4940], r0
.data 0x7C651B78 # 8000B678 => mr r5, r3
.data 0xA8FE02B8 # 8000B67C => lha r7, [r30 + 0x02B8]
.data 0x3C808000 # 8000B680 => lis r4, 0x8000
.data 0x6084B6AC # 8000B684 => ori r4, r4, 0xB6AC
.data 0x38640018 # 8000B688 => addi r3, r4, 0x0018
.data 0x4CC63182 # 8000B68C => crxor crb6, crb6, crb6
.data 0x4838BB3D # 8000B690 => bl +0x0038BB3C /* 803971CC */
.data 0x3C808000 # 8000B694 => lis r4, 0x8000
.data 0x6084B6C4 # 8000B698 => ori r4, r4, 0xB6C4
.data 0x7F83E378 # 8000B69C => mr r3, r28
.data 0x8004FFFC # 8000B6A0 => lwz r0, [r4 - 0x0004]
.data 0x7C0803A6 # 8000B6A4 => mtlr r0
.data 0x4E800020 # 8000B6A8 => blr
.data 0x25730A0A # 8000B6AC => .invalid
.data 0x48503A25 # 8000B6B0 => bl +0x00503A24 /* 8050F0D4 */
.data 0x642F2564 # 8000B6B4 => oris r15, r1, 0x2564
.data 0x00000000 # 8000B6B8 => .invalid
# region @ 80261B38 (4 bytes)
.data 0x80261B38 # address
.data 0x00000004 # size
.data 0x4BDA9B19 # 80261B38 => bl -0x002564E8 /* 8000B650 */
# region @ 80261B9C (4 bytes)
.data 0x80261B9C # address
.data 0x00000004 # size
.data 0x4BFE1545 # 80261B9C => bl -0x0001EABC /* 802430E0 */
# region @ 80261CF8 (4 bytes)
.data 0x80261CF8 # address
.data 0x00000004 # size
.data 0x4BDA996D # 80261CF8 => bl -0x00256694 /* 8000B664 */
# region @ 804CB610 (4 bytes)
.data 0x804CB610 # address
.data 0x00000004 # size
.data 0x42640000 # 804CB610 => bc 19, 4, +0x00000000 /* 804CB610 */
.data 0x42960000 # 804CB610 => bc 20, 22, +0x00000000 /* 804CB610 */
# region @ 804CB61C (4 bytes)
.data 0x804CB61C # address
.data 0x00000004 # size
.data 0x42640000 # 804CB61C => bc 19, 4, +0x00000000 /* 804CB61C */
.data 0x42960000 # 804CB61C => bc 20, 22, +0x00000000 /* 804CB61C */
# region @ 804CB628 (4 bytes)
.data 0x804CB628 # address
.data 0x00000004 # size
.data 0x42640000 # 804CB628 => bc 19, 4, +0x00000000 /* 804CB628 */
.data 0x42960000 # 804CB628 => bc 20, 22, +0x00000000 /* 804CB628 */
# region @ 804CB634 (4 bytes)
.data 0x804CB634 # address
.data 0x00000004 # size
.data 0x42640000 # 804CB634 => bc 19, 4, +0x00000000 /* 804CB634 */
.data 0x42960000 # 804CB634 => bc 20, 22, +0x00000000 /* 804CB634 */
# region @ 804CB640 (4 bytes)
.data 0x804CB640 # address
.data 0x00000004 # size
.data 0x42960000 # 804CB640 => bc 20, 22, +0x00000000 /* 804CB640 */
# region @ 804CB6D0 (4 bytes)
.data 0x804CB6D0 # address
.data 0x00000004 # size
.data 0x42300000 # 804CB6D0 => bdnz cr4, +0x00000000 /* 804CB6D0 */
.data 0x42780000 # 804CB6D0 => bc 19, 24, +0x00000000 /* 804CB6D0 */
# region @ 804CB6EC (4 bytes)
.data 0x804CB6EC # address
.data 0x00000004 # size
@@ -40,7 +82,7 @@ start:
# region @ 805CC8C4 (4 bytes)
.data 0x805CC8C4 # address
.data 0x00000004 # size
.data 0x42A00000 # 805CC8C4 => b +0x00000000 /* 805CC8C4 */
.data 0x42C00000 # 805CC8C4 => b +0x00000000 /* 805CC8C4 */
# end sentinel
.data 0x00000000 # address
.data 0x00000000 # size
@@ -9,30 +9,72 @@ reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
# region @ 8000B650 (108 bytes)
.data 0x8000B650 # address
.data 0x0000006C # size
.data 0x3CA08001 # 8000B650 => lis r5, 0x8001
.data 0x8065B6BC # 8000B654 => lwz r3, [r5 - 0x4944]
.data 0x7FFEFB78 # 8000B658 => mr r30, r31
.data 0xA8DE032C # 8000B65C => lha r6, [r30 + 0x032C]
.data 0x48000010 # 8000B660 => b +0x00000010 /* 8000B670 */
.data 0xA8DE02B8 # 8000B664 => lha r6, [r30 + 0x02B8]
.data 0x3CA08001 # 8000B668 => lis r5, 0x8001
.data 0x9065B6BC # 8000B66C => stw [r5 - 0x4944], r3
.data 0x7C0802A6 # 8000B670 => mflr r0
.data 0x9005B6C0 # 8000B674 => stw [r5 - 0x4940], r0
.data 0x7C651B78 # 8000B678 => mr r5, r3
.data 0xA8FE02B8 # 8000B67C => lha r7, [r30 + 0x02B8]
.data 0x3C808000 # 8000B680 => lis r4, 0x8000
.data 0x6084B6AC # 8000B684 => ori r4, r4, 0xB6AC
.data 0x38640018 # 8000B688 => addi r3, r4, 0x0018
.data 0x4CC63182 # 8000B68C => crxor crb6, crb6, crb6
.data 0x4838BB95 # 8000B690 => bl +0x0038BB94 /* 80397224 */
.data 0x3C808000 # 8000B694 => lis r4, 0x8000
.data 0x6084B6C4 # 8000B698 => ori r4, r4, 0xB6C4
.data 0x7F83E378 # 8000B69C => mr r3, r28
.data 0x8004FFFC # 8000B6A0 => lwz r0, [r4 - 0x0004]
.data 0x7C0803A6 # 8000B6A4 => mtlr r0
.data 0x4E800020 # 8000B6A8 => blr
.data 0x25730A0A # 8000B6AC => .invalid
.data 0x48503A25 # 8000B6B0 => bl +0x00503A24 /* 8050F0D4 */
.data 0x642F2564 # 8000B6B4 => oris r15, r1, 0x2564
.data 0x00000000 # 8000B6B8 => .invalid
# region @ 80261B38 (4 bytes)
.data 0x80261B38 # address
.data 0x00000004 # size
.data 0x4BDA9B19 # 80261B38 => bl -0x002564E8 /* 8000B650 */
# region @ 80261B9C (4 bytes)
.data 0x80261B9C # address
.data 0x00000004 # size
.data 0x4BFE1545 # 80261B9C => bl -0x0001EABC /* 802430E0 */
# region @ 80261CF8 (4 bytes)
.data 0x80261CF8 # address
.data 0x00000004 # size
.data 0x4BDA996D # 80261CF8 => bl -0x00256694 /* 8000B664 */
# region @ 804CBAF0 (4 bytes)
.data 0x804CBAF0 # address
.data 0x00000004 # size
.data 0x42640000 # 804CBAF0 => bc 19, 4, +0x00000000 /* 804CBAF0 */
.data 0x42960000 # 804CBAF0 => bc 20, 22, +0x00000000 /* 804CBAF0 */
# region @ 804CBAFC (4 bytes)
.data 0x804CBAFC # address
.data 0x00000004 # size
.data 0x42640000 # 804CBAFC => bc 19, 4, +0x00000000 /* 804CBAFC */
.data 0x42960000 # 804CBAFC => bc 20, 22, +0x00000000 /* 804CBAFC */
# region @ 804CBB08 (4 bytes)
.data 0x804CBB08 # address
.data 0x00000004 # size
.data 0x42640000 # 804CBB08 => bc 19, 4, +0x00000000 /* 804CBB08 */
.data 0x42960000 # 804CBB08 => bc 20, 22, +0x00000000 /* 804CBB08 */
# region @ 804CBB14 (4 bytes)
.data 0x804CBB14 # address
.data 0x00000004 # size
.data 0x42640000 # 804CBB14 => bc 19, 4, +0x00000000 /* 804CBB14 */
.data 0x42960000 # 804CBB14 => bc 20, 22, +0x00000000 /* 804CBB14 */
# region @ 804CBB20 (4 bytes)
.data 0x804CBB20 # address
.data 0x00000004 # size
.data 0x42960000 # 804CBB20 => bc 20, 22, +0x00000000 /* 804CBB20 */
# region @ 804CBBB0 (4 bytes)
.data 0x804CBBB0 # address
.data 0x00000004 # size
.data 0x42300000 # 804CBBB0 => bdnz cr4, +0x00000000 /* 804CBBB0 */
.data 0x42780000 # 804CBBB0 => bc 19, 24, +0x00000000 /* 804CBBB0 */
# region @ 804CBBCC (4 bytes)
.data 0x804CBBCC # address
.data 0x00000004 # size
@@ -40,7 +82,7 @@ start:
# region @ 805D38E4 (4 bytes)
.data 0x805D38E4 # address
.data 0x00000004 # size
.data 0x42A00000 # 805D38E4 => b +0x00000000 /* 805D38E4 */
.data 0x42C00000 # 805D38E4 => b +0x00000000 /* 805D38E4 */
# end sentinel
.data 0x00000000 # address
.data 0x00000000 # size
@@ -9,30 +9,72 @@ reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
# region @ 8000B650 (108 bytes)
.data 0x8000B650 # address
.data 0x0000006C # size
.data 0x3CA08001 # 8000B650 => lis r5, 0x8001
.data 0x8065B6BC # 8000B654 => lwz r3, [r5 - 0x4944]
.data 0x7FFEFB78 # 8000B658 => mr r30, r31
.data 0xA8DE032C # 8000B65C => lha r6, [r30 + 0x032C]
.data 0x48000010 # 8000B660 => b +0x00000010 /* 8000B670 */
.data 0xA8DE02B8 # 8000B664 => lha r6, [r30 + 0x02B8]
.data 0x3CA08001 # 8000B668 => lis r5, 0x8001
.data 0x9065B6BC # 8000B66C => stw [r5 - 0x4944], r3
.data 0x7C0802A6 # 8000B670 => mflr r0
.data 0x9005B6C0 # 8000B674 => stw [r5 - 0x4940], r0
.data 0x7C651B78 # 8000B678 => mr r5, r3
.data 0xA8FE02B8 # 8000B67C => lha r7, [r30 + 0x02B8]
.data 0x3C808000 # 8000B680 => lis r4, 0x8000
.data 0x6084B6AC # 8000B684 => ori r4, r4, 0xB6AC
.data 0x38640018 # 8000B688 => addi r3, r4, 0x0018
.data 0x4CC63182 # 8000B68C => crxor crb6, crb6, crb6
.data 0x4838F295 # 8000B690 => bl +0x0038F294 /* 8039A924 */
.data 0x3C808000 # 8000B694 => lis r4, 0x8000
.data 0x6084B6C4 # 8000B698 => ori r4, r4, 0xB6C4
.data 0x7F83E378 # 8000B69C => mr r3, r28
.data 0x8004FFFC # 8000B6A0 => lwz r0, [r4 - 0x0004]
.data 0x7C0803A6 # 8000B6A4 => mtlr r0
.data 0x4E800020 # 8000B6A8 => blr
.data 0x25730A0A # 8000B6AC => .invalid
.data 0x48503A25 # 8000B6B0 => bl +0x00503A24 /* 8050F0D4 */
.data 0x642F2564 # 8000B6B4 => oris r15, r1, 0x2564
.data 0x00000000 # 8000B6B8 => .invalid
# region @ 80262EF8 (4 bytes)
.data 0x80262EF8 # address
.data 0x00000004 # size
.data 0x4BDA8759 # 80262EF8 => bl -0x002578A8 /* 8000B650 */
# region @ 80262F5C (4 bytes)
.data 0x80262F5C # address
.data 0x00000004 # size
.data 0x4BFE12B1 # 80262F5C => bl -0x0001ED50 /* 8024420C */
# region @ 802630B8 (4 bytes)
.data 0x802630B8 # address
.data 0x00000004 # size
.data 0x4BDA85AD # 802630B8 => bl -0x00257A54 /* 8000B664 */
# region @ 804D0158 (4 bytes)
.data 0x804D0158 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0158 => bc 19, 4, +0x00000000 /* 804D0158 */
.data 0x42960000 # 804D0158 => bc 20, 22, +0x00000000 /* 804D0158 */
# region @ 804D0164 (4 bytes)
.data 0x804D0164 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0164 => bc 19, 4, +0x00000000 /* 804D0164 */
.data 0x42960000 # 804D0164 => bc 20, 22, +0x00000000 /* 804D0164 */
# region @ 804D0170 (4 bytes)
.data 0x804D0170 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0170 => bc 19, 4, +0x00000000 /* 804D0170 */
.data 0x42960000 # 804D0170 => bc 20, 22, +0x00000000 /* 804D0170 */
# region @ 804D017C (4 bytes)
.data 0x804D017C # address
.data 0x00000004 # size
.data 0x42640000 # 804D017C => bc 19, 4, +0x00000000 /* 804D017C */
.data 0x42960000 # 804D017C => bc 20, 22, +0x00000000 /* 804D017C */
# region @ 804D0188 (4 bytes)
.data 0x804D0188 # address
.data 0x00000004 # size
.data 0x42960000 # 804D0188 => bc 20, 22, +0x00000000 /* 804D0188 */
# region @ 804D0218 (4 bytes)
.data 0x804D0218 # address
.data 0x00000004 # size
.data 0x42300000 # 804D0218 => bdnz cr4, +0x00000000 /* 804D0218 */
.data 0x42780000 # 804D0218 => bc 19, 24, +0x00000000 /* 804D0218 */
# region @ 804D0234 (4 bytes)
.data 0x804D0234 # address
.data 0x00000004 # size
@@ -40,7 +82,7 @@ start:
# region @ 805DD104 (4 bytes)
.data 0x805DD104 # address
.data 0x00000004 # size
.data 0x42A00000 # 805DD104 => b +0x00000000 /* 805DD104 */
.data 0x42C00000 # 805DD104 => b +0x00000000 /* 805DD104 */
# end sentinel
.data 0x00000000 # address
.data 0x00000000 # size
@@ -9,30 +9,72 @@ reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
# region @ 8000B650 (108 bytes)
.data 0x8000B650 # address
.data 0x0000006C # size
.data 0x3CA08001 # 8000B650 => lis r5, 0x8001
.data 0x8065B6BC # 8000B654 => lwz r3, [r5 - 0x4944]
.data 0x7FFEFB78 # 8000B658 => mr r30, r31
.data 0xA8DE032C # 8000B65C => lha r6, [r30 + 0x032C]
.data 0x48000010 # 8000B660 => b +0x00000010 /* 8000B670 */
.data 0xA8DE02B8 # 8000B664 => lha r6, [r30 + 0x02B8]
.data 0x3CA08001 # 8000B668 => lis r5, 0x8001
.data 0x9065B6BC # 8000B66C => stw [r5 - 0x4944], r3
.data 0x7C0802A6 # 8000B670 => mflr r0
.data 0x9005B6C0 # 8000B674 => stw [r5 - 0x4940], r0
.data 0x7C651B78 # 8000B678 => mr r5, r3
.data 0xA8FE02B8 # 8000B67C => lha r7, [r30 + 0x02B8]
.data 0x3C808000 # 8000B680 => lis r4, 0x8000
.data 0x6084B6AC # 8000B684 => ori r4, r4, 0xB6AC
.data 0x38640018 # 8000B688 => addi r3, r4, 0x0018
.data 0x4CC63182 # 8000B68C => crxor crb6, crb6, crb6
.data 0x4838A86D # 8000B690 => bl +0x0038A86C /* 80395EFC */
.data 0x3C808000 # 8000B694 => lis r4, 0x8000
.data 0x6084B6C4 # 8000B698 => ori r4, r4, 0xB6C4
.data 0x7F83E378 # 8000B69C => mr r3, r28
.data 0x8004FFFC # 8000B6A0 => lwz r0, [r4 - 0x0004]
.data 0x7C0803A6 # 8000B6A4 => mtlr r0
.data 0x4E800020 # 8000B6A8 => blr
.data 0x25730A0A # 8000B6AC => .invalid
.data 0x48503A25 # 8000B6B0 => bl +0x00503A24 /* 8050F0D4 */
.data 0x642F2564 # 8000B6B4 => oris r15, r1, 0x2564
.data 0x00000000 # 8000B6B8 => .invalid
# region @ 80261260 (4 bytes)
.data 0x80261260 # address
.data 0x00000004 # size
.data 0x4BDAA3F1 # 80261260 => bl -0x00255C10 /* 8000B650 */
# region @ 802612C4 (4 bytes)
.data 0x802612C4 # address
.data 0x00000004 # size
.data 0x4BFE1541 # 802612C4 => bl -0x0001EAC0 /* 80242804 */
# region @ 80261420 (4 bytes)
.data 0x80261420 # address
.data 0x00000004 # size
.data 0x4BDAA245 # 80261420 => bl -0x00255DBC /* 8000B664 */
# region @ 804CAE40 (4 bytes)
.data 0x804CAE40 # address
.data 0x00000004 # size
.data 0x42640000 # 804CAE40 => bc 19, 4, +0x00000000 /* 804CAE40 */
.data 0x42960000 # 804CAE40 => bc 20, 22, +0x00000000 /* 804CAE40 */
# region @ 804CAE4C (4 bytes)
.data 0x804CAE4C # address
.data 0x00000004 # size
.data 0x42640000 # 804CAE4C => bc 19, 4, +0x00000000 /* 804CAE4C */
.data 0x42960000 # 804CAE4C => bc 20, 22, +0x00000000 /* 804CAE4C */
# region @ 804CAE58 (4 bytes)
.data 0x804CAE58 # address
.data 0x00000004 # size
.data 0x42640000 # 804CAE58 => bc 19, 4, +0x00000000 /* 804CAE58 */
.data 0x42960000 # 804CAE58 => bc 20, 22, +0x00000000 /* 804CAE58 */
# region @ 804CAE64 (4 bytes)
.data 0x804CAE64 # address
.data 0x00000004 # size
.data 0x42640000 # 804CAE64 => bc 19, 4, +0x00000000 /* 804CAE64 */
.data 0x42960000 # 804CAE64 => bc 20, 22, +0x00000000 /* 804CAE64 */
# region @ 804CAE70 (4 bytes)
.data 0x804CAE70 # address
.data 0x00000004 # size
.data 0x42960000 # 804CAE70 => bc 20, 22, +0x00000000 /* 804CAE70 */
# region @ 804CAF00 (4 bytes)
.data 0x804CAF00 # address
.data 0x00000004 # size
.data 0x42300000 # 804CAF00 => bdnz cr4, +0x00000000 /* 804CAF00 */
.data 0x42780000 # 804CAF00 => bc 19, 24, +0x00000000 /* 804CAF00 */
# region @ 804CAF1C (4 bytes)
.data 0x804CAF1C # address
.data 0x00000004 # size
@@ -40,7 +82,7 @@ start:
# region @ 805CBFBC (4 bytes)
.data 0x805CBFBC # address
.data 0x00000004 # size
.data 0x42A00000 # 805CBFBC => b +0x00000000 /* 805CBFBC */
.data 0x42C00000 # 805CBFBC => b +0x00000000 /* 805CBFBC */
# end sentinel
.data 0x00000000 # address
.data 0x00000000 # size
@@ -9,30 +9,72 @@ reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
# region @ 8000B650 (108 bytes)
.data 0x8000B650 # address
.data 0x0000006C # size
.data 0x3CA08001 # 8000B650 => lis r5, 0x8001
.data 0x8065B6BC # 8000B654 => lwz r3, [r5 - 0x4944]
.data 0x7FFEFB78 # 8000B658 => mr r30, r31
.data 0xA8DE032C # 8000B65C => lha r6, [r30 + 0x032C]
.data 0x48000010 # 8000B660 => b +0x00000010 /* 8000B670 */
.data 0xA8DE02B8 # 8000B664 => lha r6, [r30 + 0x02B8]
.data 0x3CA08001 # 8000B668 => lis r5, 0x8001
.data 0x9065B6BC # 8000B66C => stw [r5 - 0x4944], r3
.data 0x7C0802A6 # 8000B670 => mflr r0
.data 0x9005B6C0 # 8000B674 => stw [r5 - 0x4940], r0
.data 0x7C651B78 # 8000B678 => mr r5, r3
.data 0xA8FE02B8 # 8000B67C => lha r7, [r30 + 0x02B8]
.data 0x3C808000 # 8000B680 => lis r4, 0x8000
.data 0x6084B6AC # 8000B684 => ori r4, r4, 0xB6AC
.data 0x38640018 # 8000B688 => addi r3, r4, 0x0018
.data 0x4CC63182 # 8000B68C => crxor crb6, crb6, crb6
.data 0x4838D275 # 8000B690 => bl +0x0038D274 /* 80398904 */
.data 0x3C808000 # 8000B694 => lis r4, 0x8000
.data 0x6084B6C4 # 8000B698 => ori r4, r4, 0xB6C4
.data 0x7F83E378 # 8000B69C => mr r3, r28
.data 0x8004FFFC # 8000B6A0 => lwz r0, [r4 - 0x0004]
.data 0x7C0803A6 # 8000B6A4 => mtlr r0
.data 0x4E800020 # 8000B6A8 => blr
.data 0x25730A0A # 8000B6AC => .invalid
.data 0x48503A25 # 8000B6B0 => bl +0x00503A24 /* 8050F0D4 */
.data 0x642F2564 # 8000B6B4 => oris r15, r1, 0x2564
.data 0x00000000 # 8000B6B8 => .invalid
# region @ 80261E38 (4 bytes)
.data 0x80261E38 # address
.data 0x00000004 # size
.data 0x4BDA9819 # 80261E38 => bl -0x002567E8 /* 8000B650 */
# region @ 80261E9C (4 bytes)
.data 0x80261E9C # address
.data 0x00000004 # size
.data 0x4BFE1349 # 80261E9C => bl -0x0001ECB8 /* 802431E4 */
# region @ 80261FF8 (4 bytes)
.data 0x80261FF8 # address
.data 0x00000004 # size
.data 0x4BDA966D # 80261FF8 => bl -0x00256994 /* 8000B664 */
# region @ 804CE590 (4 bytes)
.data 0x804CE590 # address
.data 0x00000004 # size
.data 0x42640000 # 804CE590 => bc 19, 4, +0x00000000 /* 804CE590 */
.data 0x42960000 # 804CE590 => bc 20, 22, +0x00000000 /* 804CE590 */
# region @ 804CE59C (4 bytes)
.data 0x804CE59C # address
.data 0x00000004 # size
.data 0x42640000 # 804CE59C => bc 19, 4, +0x00000000 /* 804CE59C */
.data 0x42960000 # 804CE59C => bc 20, 22, +0x00000000 /* 804CE59C */
# region @ 804CE5A8 (4 bytes)
.data 0x804CE5A8 # address
.data 0x00000004 # size
.data 0x42640000 # 804CE5A8 => bc 19, 4, +0x00000000 /* 804CE5A8 */
.data 0x42960000 # 804CE5A8 => bc 20, 22, +0x00000000 /* 804CE5A8 */
# region @ 804CE5B4 (4 bytes)
.data 0x804CE5B4 # address
.data 0x00000004 # size
.data 0x42640000 # 804CE5B4 => bc 19, 4, +0x00000000 /* 804CE5B4 */
.data 0x42960000 # 804CE5B4 => bc 20, 22, +0x00000000 /* 804CE5B4 */
# region @ 804CE5C0 (4 bytes)
.data 0x804CE5C0 # address
.data 0x00000004 # size
.data 0x42960000 # 804CE5C0 => bc 20, 22, +0x00000000 /* 804CE5C0 */
# region @ 804CE650 (4 bytes)
.data 0x804CE650 # address
.data 0x00000004 # size
.data 0x42300000 # 804CE650 => bdnz cr4, +0x00000000 /* 804CE650 */
.data 0x42780000 # 804CE650 => bc 19, 24, +0x00000000 /* 804CE650 */
# region @ 804CE66C (4 bytes)
.data 0x804CE66C # address
.data 0x00000004 # size
@@ -40,7 +82,7 @@ start:
# region @ 805D65BC (4 bytes)
.data 0x805D65BC # address
.data 0x00000004 # size
.data 0x42A00000 # 805D65BC => b +0x00000000 /* 805D65BC */
.data 0x42C00000 # 805D65BC => b +0x00000000 /* 805D65BC */
# end sentinel
.data 0x00000000 # address
.data 0x00000000 # size
@@ -9,30 +9,72 @@ reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
# region @ 8000B650 (108 bytes)
.data 0x8000B650 # address
.data 0x0000006C # size
.data 0x3CA08001 # 8000B650 => lis r5, 0x8001
.data 0x8065B6BC # 8000B654 => lwz r3, [r5 - 0x4944]
.data 0x7FFEFB78 # 8000B658 => mr r30, r31
.data 0xA8DE032C # 8000B65C => lha r6, [r30 + 0x032C]
.data 0x48000010 # 8000B660 => b +0x00000010 /* 8000B670 */
.data 0xA8DE02B8 # 8000B664 => lha r6, [r30 + 0x02B8]
.data 0x3CA08001 # 8000B668 => lis r5, 0x8001
.data 0x9065B6BC # 8000B66C => stw [r5 - 0x4944], r3
.data 0x7C0802A6 # 8000B670 => mflr r0
.data 0x9005B6C0 # 8000B674 => stw [r5 - 0x4940], r0
.data 0x7C651B78 # 8000B678 => mr r5, r3
.data 0xA8FE02B8 # 8000B67C => lha r7, [r30 + 0x02B8]
.data 0x3C808000 # 8000B680 => lis r4, 0x8000
.data 0x6084B6AC # 8000B684 => ori r4, r4, 0xB6AC
.data 0x38640018 # 8000B688 => addi r3, r4, 0x0018
.data 0x4CC63182 # 8000B68C => crxor crb6, crb6, crb6
.data 0x4838F115 # 8000B690 => bl +0x0038F114 /* 8039A7A4 */
.data 0x3C808000 # 8000B694 => lis r4, 0x8000
.data 0x6084B6C4 # 8000B698 => ori r4, r4, 0xB6C4
.data 0x7F83E378 # 8000B69C => mr r3, r28
.data 0x8004FFFC # 8000B6A0 => lwz r0, [r4 - 0x0004]
.data 0x7C0803A6 # 8000B6A4 => mtlr r0
.data 0x4E800020 # 8000B6A8 => blr
.data 0x25730A0A # 8000B6AC => .invalid
.data 0x48503A25 # 8000B6B0 => bl +0x00503A24 /* 8050F0D4 */
.data 0x642F2564 # 8000B6B4 => oris r15, r1, 0x2564
.data 0x00000000 # 8000B6B8 => .invalid
# region @ 80262E80 (4 bytes)
.data 0x80262E80 # address
.data 0x00000004 # size
.data 0x4BDA87D1 # 80262E80 => bl -0x00257830 /* 8000B650 */
# region @ 80262EE4 (4 bytes)
.data 0x80262EE4 # address
.data 0x00000004 # size
.data 0x4BFE0665 # 80262EE4 => bl -0x0001F99C /* 80243548 */
# region @ 80263040 (4 bytes)
.data 0x80263040 # address
.data 0x00000004 # size
.data 0x4BDA8625 # 80263040 => bl -0x002579DC /* 8000B664 */
# region @ 804D0AE0 (4 bytes)
.data 0x804D0AE0 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0AE0 => bc 19, 4, +0x00000000 /* 804D0AE0 */
.data 0x42960000 # 804D0AE0 => bc 20, 22, +0x00000000 /* 804D0AE0 */
# region @ 804D0AEC (4 bytes)
.data 0x804D0AEC # address
.data 0x00000004 # size
.data 0x42640000 # 804D0AEC => bc 19, 4, +0x00000000 /* 804D0AEC */
.data 0x42960000 # 804D0AEC => bc 20, 22, +0x00000000 /* 804D0AEC */
# region @ 804D0AF8 (4 bytes)
.data 0x804D0AF8 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0AF8 => bc 19, 4, +0x00000000 /* 804D0AF8 */
.data 0x42960000 # 804D0AF8 => bc 20, 22, +0x00000000 /* 804D0AF8 */
# region @ 804D0B04 (4 bytes)
.data 0x804D0B04 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0B04 => bc 19, 4, +0x00000000 /* 804D0B04 */
.data 0x42960000 # 804D0B04 => bc 20, 22, +0x00000000 /* 804D0B04 */
# region @ 804D0B10 (4 bytes)
.data 0x804D0B10 # address
.data 0x00000004 # size
.data 0x42960000 # 804D0B10 => bc 20, 22, +0x00000000 /* 804D0B10 */
# region @ 804D0BA0 (4 bytes)
.data 0x804D0BA0 # address
.data 0x00000004 # size
.data 0x42300000 # 804D0BA0 => bdnz cr4, +0x00000000 /* 804D0BA0 */
.data 0x42780000 # 804D0BA0 => bc 19, 24, +0x00000000 /* 804D0BA0 */
# region @ 804D0BBC (4 bytes)
.data 0x804D0BBC # address
.data 0x00000004 # size
@@ -40,7 +82,7 @@ start:
# region @ 805DDA5C (4 bytes)
.data 0x805DDA5C # address
.data 0x00000004 # size
.data 0x42A00000 # 805DDA5C => b +0x00000000 /* 805DDA5C */
.data 0x42C00000 # 805DDA5C => b +0x00000000 /* 805DDA5C */
# end sentinel
.data 0x00000000 # address
.data 0x00000000 # size
@@ -9,30 +9,72 @@ reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
# region @ 8000B650 (108 bytes)
.data 0x8000B650 # address
.data 0x0000006C # size
.data 0x3CA08001 # 8000B650 => lis r5, 0x8001
.data 0x8065B6BC # 8000B654 => lwz r3, [r5 - 0x4944]
.data 0x7FFEFB78 # 8000B658 => mr r30, r31
.data 0xA8DE032C # 8000B65C => lha r6, [r30 + 0x032C]
.data 0x48000010 # 8000B660 => b +0x00000010 /* 8000B670 */
.data 0xA8DE02B8 # 8000B664 => lha r6, [r30 + 0x02B8]
.data 0x3CA08001 # 8000B668 => lis r5, 0x8001
.data 0x9065B6BC # 8000B66C => stw [r5 - 0x4944], r3
.data 0x7C0802A6 # 8000B670 => mflr r0
.data 0x9005B6C0 # 8000B674 => stw [r5 - 0x4940], r0
.data 0x7C651B78 # 8000B678 => mr r5, r3
.data 0xA8FE02B8 # 8000B67C => lha r7, [r30 + 0x02B8]
.data 0x3C808000 # 8000B680 => lis r4, 0x8000
.data 0x6084B6AC # 8000B684 => ori r4, r4, 0xB6AC
.data 0x38640018 # 8000B688 => addi r3, r4, 0x0018
.data 0x4CC63182 # 8000B68C => crxor crb6, crb6, crb6
.data 0x4838EEC5 # 8000B690 => bl +0x0038EEC4 /* 8039A554 */
.data 0x3C808000 # 8000B694 => lis r4, 0x8000
.data 0x6084B6C4 # 8000B698 => ori r4, r4, 0xB6C4
.data 0x7F83E378 # 8000B69C => mr r3, r28
.data 0x8004FFFC # 8000B6A0 => lwz r0, [r4 - 0x0004]
.data 0x7C0803A6 # 8000B6A4 => mtlr r0
.data 0x4E800020 # 8000B6A8 => blr
.data 0x25730A0A # 8000B6AC => .invalid
.data 0x48503A25 # 8000B6B0 => bl +0x00503A24 /* 8050F0D4 */
.data 0x642F2564 # 8000B6B4 => oris r15, r1, 0x2564
.data 0x00000000 # 8000B6B8 => .invalid
# region @ 80262C34 (4 bytes)
.data 0x80262C34 # address
.data 0x00000004 # size
.data 0x4BDA8A1D # 80262C34 => bl -0x002575E4 /* 8000B650 */
# region @ 80262C98 (4 bytes)
.data 0x80262C98 # address
.data 0x00000004 # size
.data 0x4BFE1241 # 80262C98 => bl -0x0001EDC0 /* 80243ED8 */
# region @ 80262DF4 (4 bytes)
.data 0x80262DF4 # address
.data 0x00000004 # size
.data 0x4BDA8871 # 80262DF4 => bl -0x00257790 /* 8000B664 */
# region @ 804D0880 (4 bytes)
.data 0x804D0880 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0880 => bc 19, 4, +0x00000000 /* 804D0880 */
.data 0x42960000 # 804D0880 => bc 20, 22, +0x00000000 /* 804D0880 */
# region @ 804D088C (4 bytes)
.data 0x804D088C # address
.data 0x00000004 # size
.data 0x42640000 # 804D088C => bc 19, 4, +0x00000000 /* 804D088C */
.data 0x42960000 # 804D088C => bc 20, 22, +0x00000000 /* 804D088C */
# region @ 804D0898 (4 bytes)
.data 0x804D0898 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0898 => bc 19, 4, +0x00000000 /* 804D0898 */
.data 0x42960000 # 804D0898 => bc 20, 22, +0x00000000 /* 804D0898 */
# region @ 804D08A4 (4 bytes)
.data 0x804D08A4 # address
.data 0x00000004 # size
.data 0x42640000 # 804D08A4 => bc 19, 4, +0x00000000 /* 804D08A4 */
.data 0x42960000 # 804D08A4 => bc 20, 22, +0x00000000 /* 804D08A4 */
# region @ 804D08B0 (4 bytes)
.data 0x804D08B0 # address
.data 0x00000004 # size
.data 0x42960000 # 804D08B0 => bc 20, 22, +0x00000000 /* 804D08B0 */
# region @ 804D0940 (4 bytes)
.data 0x804D0940 # address
.data 0x00000004 # size
.data 0x42300000 # 804D0940 => bdnz cr4, +0x00000000 /* 804D0940 */
.data 0x42780000 # 804D0940 => bc 19, 24, +0x00000000 /* 804D0940 */
# region @ 804D095C (4 bytes)
.data 0x804D095C # address
.data 0x00000004 # size
@@ -40,7 +82,7 @@ start:
# region @ 805DD7FC (4 bytes)
.data 0x805DD7FC # address
.data 0x00000004 # size
.data 0x42A00000 # 805DD7FC => b +0x00000000 /* 805DD7FC */
.data 0x42C00000 # 805DD7FC => b +0x00000000 /* 805DD7FC */
# end sentinel
.data 0x00000000 # address
.data 0x00000000 # size
@@ -9,30 +9,72 @@ reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
# region @ 8000B650 (108 bytes)
.data 0x8000B650 # address
.data 0x0000006C # size
.data 0x3CA08001 # 8000B650 => lis r5, 0x8001
.data 0x8065B6BC # 8000B654 => lwz r3, [r5 - 0x4944]
.data 0x7FFEFB78 # 8000B658 => mr r30, r31
.data 0xA8DE032C # 8000B65C => lha r6, [r30 + 0x032C]
.data 0x48000010 # 8000B660 => b +0x00000010 /* 8000B670 */
.data 0xA8DE02B8 # 8000B664 => lha r6, [r30 + 0x02B8]
.data 0x3CA08001 # 8000B668 => lis r5, 0x8001
.data 0x9065B6BC # 8000B66C => stw [r5 - 0x4944], r3
.data 0x7C0802A6 # 8000B670 => mflr r0
.data 0x9005B6C0 # 8000B674 => stw [r5 - 0x4940], r0
.data 0x7C651B78 # 8000B678 => mr r5, r3
.data 0xA8FE02B8 # 8000B67C => lha r7, [r30 + 0x02B8]
.data 0x3C808000 # 8000B680 => lis r4, 0x8000
.data 0x6084B6AC # 8000B684 => ori r4, r4, 0xB6AC
.data 0x38640018 # 8000B688 => addi r3, r4, 0x0018
.data 0x4CC63182 # 8000B68C => crxor crb6, crb6, crb6
.data 0x4838DD85 # 8000B690 => bl +0x0038DD84 /* 80399414 */
.data 0x3C808000 # 8000B694 => lis r4, 0x8000
.data 0x6084B6C4 # 8000B698 => ori r4, r4, 0xB6C4
.data 0x7F83E378 # 8000B69C => mr r3, r28
.data 0x8004FFFC # 8000B6A0 => lwz r0, [r4 - 0x0004]
.data 0x7C0803A6 # 8000B6A4 => mtlr r0
.data 0x4E800020 # 8000B6A8 => blr
.data 0x25730A0A # 8000B6AC => .invalid
.data 0x48503A25 # 8000B6B0 => bl +0x00503A24 /* 8050F0D4 */
.data 0x642F2564 # 8000B6B4 => oris r15, r1, 0x2564
.data 0x00000000 # 8000B6B8 => .invalid
# region @ 80262740 (4 bytes)
.data 0x80262740 # address
.data 0x00000004 # size
.data 0x4BDA8F11 # 80262740 => bl -0x002570F0 /* 8000B650 */
# region @ 802627A4 (4 bytes)
.data 0x802627A4 # address
.data 0x00000004 # size
.data 0x4BFE12B1 # 802627A4 => bl -0x0001ED50 /* 80243A54 */
# region @ 80262900 (4 bytes)
.data 0x80262900 # address
.data 0x00000004 # size
.data 0x4BDA8D65 # 80262900 => bl -0x0025729C /* 8000B664 */
# region @ 804D0548 (4 bytes)
.data 0x804D0548 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0548 => bc 19, 4, +0x00000000 /* 804D0548 */
.data 0x42960000 # 804D0548 => bc 20, 22, +0x00000000 /* 804D0548 */
# region @ 804D0554 (4 bytes)
.data 0x804D0554 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0554 => bc 19, 4, +0x00000000 /* 804D0554 */
.data 0x42960000 # 804D0554 => bc 20, 22, +0x00000000 /* 804D0554 */
# region @ 804D0560 (4 bytes)
.data 0x804D0560 # address
.data 0x00000004 # size
.data 0x42640000 # 804D0560 => bc 19, 4, +0x00000000 /* 804D0560 */
.data 0x42960000 # 804D0560 => bc 20, 22, +0x00000000 /* 804D0560 */
# region @ 804D056C (4 bytes)
.data 0x804D056C # address
.data 0x00000004 # size
.data 0x42640000 # 804D056C => bc 19, 4, +0x00000000 /* 804D056C */
.data 0x42960000 # 804D056C => bc 20, 22, +0x00000000 /* 804D056C */
# region @ 804D0578 (4 bytes)
.data 0x804D0578 # address
.data 0x00000004 # size
.data 0x42960000 # 804D0578 => bc 20, 22, +0x00000000 /* 804D0578 */
# region @ 804D0608 (4 bytes)
.data 0x804D0608 # address
.data 0x00000004 # size
.data 0x42300000 # 804D0608 => bdnz cr4, +0x00000000 /* 804D0608 */
.data 0x42780000 # 804D0608 => bc 19, 24, +0x00000000 /* 804D0608 */
# region @ 804D0624 (4 bytes)
.data 0x804D0624 # address
.data 0x00000004 # size
@@ -40,7 +82,7 @@ start:
# region @ 805D9344 (4 bytes)
.data 0x805D9344 # address
.data 0x00000004 # size
.data 0x42A00000 # 805D9344 => b +0x00000000 /* 805D9344 */
.data 0x42C00000 # 805D9344 => b +0x00000000 /* 805D9344 */
# end sentinel
.data 0x00000000 # address
.data 0x00000000 # size
@@ -12,30 +12,58 @@ start:
.include WriteCodeBlocksXB
.data 0x0026B063
.data 0x00000001
.binary A0
.binary C0
.data 0x0026B06C
.data 0x00000001
.binary DA
.binary FA
.data 0x0026B266
.data 0x00000004
.binary 836004FD
.data 0x0054A92C
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A938
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A944
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A950
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A9EC
.data 0x00000004
.data 0x42300000
.data 0x42780000
.data 0x0054AA08
.data 0x00000004
.data 0xFF00FF15
.data 0x00010C00
.deltaof str_data_start, str_data_end
str_data_start:
.data 0x00318308 # sprintf
.data 0x00264E80 # Original function for on_window_created callsite
.data 0x00000000
.binary "%s\n\nHP:%d/%d"
.data 0x00000000
.data 0x00000000
str_data_end:
.data 0x002DB050
.deltaof new_code_start, new_code_end
new_code_start:
.include EnemyHPBars-TextHandlerXB
new_code_end:
.data 0x0026B241
.data 0x00000007
nop
nop
.binary E80BFE0600 # call 002DB053 (on_hp_updated)
.data 0x0026B028
.data 0x00000005
.binary E824000700 # call 002DB051 (on_window_created)
.data 0x00000000
.data 0x00000000
@@ -12,30 +12,58 @@ start:
.include WriteCodeBlocksXB
.data 0x0026B193
.data 0x00000001
.binary A0
.binary C0
.data 0x0026B19C
.data 0x00000001
.binary DA
.binary FA
.data 0x0026B396
.data 0x00000004
.binary 836004FD
.data 0x0054A1CC
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A1D8
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A1E4
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A1F0
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A28C
.data 0x00000004
.data 0x42300000
.data 0x42780000
.data 0x0054A2A8
.data 0x00000004
.data 0xFF00FF15
.data 0x00010C00
.deltaof str_data_start, str_data_end
str_data_start:
.data 0x00317D7A # sprintf
.data 0x00264F80 # Original function for on_window_created callsite
.data 0x00000000
.binary "%s\n\nHP:%d/%d"
.data 0x00000000
.data 0x00000000
str_data_end:
.data 0x002DB550
.deltaof new_code_start, new_code_end
new_code_start:
.include EnemyHPBars-TextHandlerXB
new_code_end:
.data 0x0026B371
.data 0x00000007
nop
nop
.binary E8DB010700 # call 002DB553 (on_hp_updated)
.data 0x0026B158
.data 0x00000005
.binary E8F4030700 # call 002DB551 (on_window_created)
.data 0x00000000
.data 0x00000000
@@ -12,30 +12,58 @@ start:
.include WriteCodeBlocksXB
.data 0x0026ABA3
.data 0x00000001
.binary A0
.binary C0
.data 0x0026ABAC
.data 0x00000001
.binary DA
.binary FA
.data 0x0026ADA6
.data 0x00000004
.binary 836004FD
.data 0x00545334
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x00545340
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054534C
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x00545358
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x005453F4
.data 0x00000004
.data 0x42300000
.data 0x42780000
.data 0x00545410
.data 0x00000004
.data 0xFF00FF15
.data 0x00010C00
.deltaof str_data_start, str_data_end
str_data_start:
.data 0x00313B22 # sprintf
.data 0x002649C0 # Original function for on_window_created callsite
.data 0x00000000
.binary "%s\n\nHP:%d/%d"
.data 0x00000000
.data 0x00000000
str_data_end:
.data 0x002D90E0
.deltaof new_code_start, new_code_end
new_code_start:
.include EnemyHPBars-TextHandlerXB
new_code_end:
.data 0x0026AD81
.data 0x00000007
nop
nop
.binary E85BE30600 # call 002D90E3 (on_hp_updated)
.data 0x0026AB68
.data 0x00000005
.binary E874E50600 # call 002D90E1 (on_window_created)
.data 0x00000000
.data 0x00000000
@@ -12,30 +12,58 @@ start:
.include WriteCodeBlocksXB
.data 0x0026AF13
.data 0x00000001
.binary A0
.binary C0
.data 0x0026AF1C
.data 0x00000001
.binary DA
.binary FA
.data 0x0026B116
.data 0x00000004
.binary 836004FD
.data 0x005459C4
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x005459D0
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x005459DC
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x005459E8
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x00545A84
.data 0x00000004
.data 0x42300000
.data 0x42780000
.data 0x00545AA0
.data 0x00000004
.data 0xFF00FF15
.data 0x00010C00
.deltaof str_data_start, str_data_end
str_data_start:
.data 0x00314722 # sprintf
.data 0x00264D80 # Original function for on_window_created callsite
.data 0x00000000
.binary "%s\n\nHP:%d/%d"
.data 0x00000000
.data 0x00000000
str_data_end:
.data 0x002D9CB0
.deltaof new_code_start, new_code_end
new_code_start:
.include EnemyHPBars-TextHandlerXB
new_code_end:
.data 0x0026B0F1
.data 0x00000007
nop
nop
.binary E8BBEB0600 # call 002D9CB3 (on_hp_updated)
.data 0x0026AED8
.data 0x00000005
.binary E8D4ED0600 # call 002D9CB1 (on_window_created)
.data 0x00000000
.data 0x00000000
@@ -12,30 +12,58 @@ start:
.include WriteCodeBlocksXB
.data 0x0026B2F3
.data 0x00000001
.binary A0
.binary C0
.data 0x0026B2FC
.data 0x00000001
.binary DA
.binary FA
.data 0x0026B4F6
.data 0x00000004
.binary 836004FD
.data 0x0054D4AC
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054D4B8
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054D4C4
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054D4D0
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054D56C
.data 0x00000004
.data 0x42300000
.data 0x42780000
.data 0x0054D588
.data 0x00000004
.data 0xFF00FF15
.data 0x00010C00
.deltaof str_data_start, str_data_end
str_data_start:
.data 0x00317D7A # sprintf
.data 0x00265130 # Original function for on_window_created callsite
.data 0x00000000
.binary "%s\n\nHP:%d/%d"
.data 0x00000000
.data 0x00000000
str_data_end:
.data 0x002DB580
.deltaof new_code_start, new_code_end
new_code_start:
.include EnemyHPBars-TextHandlerXB
new_code_end:
.data 0x0026B4D1
.data 0x00000007
nop
nop
.binary E8AB000700 # call 002DB583 (on_hp_updated)
.data 0x0026B2B8
.data 0x00000005
.binary E8C4020700 # call 002DB581 (on_window_created)
.data 0x00000000
.data 0x00000000
@@ -12,30 +12,58 @@ start:
.include WriteCodeBlocksXB
.data 0x0026B083
.data 0x00000001
.binary A0
.binary C0
.data 0x0026B08C
.data 0x00000001
.binary DA
.binary FA
.data 0x0026B286
.data 0x00000004
.binary 836004FD
.data 0x0054A92C
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A938
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A944
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A950
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054A9EC
.data 0x00000004
.data 0x42300000
.data 0x42780000
.data 0x0054AA08
.data 0x00000004
.data 0xFF00FF15
.data 0x00010C00
.deltaof str_data_start, str_data_end
str_data_start:
.data 0x00318338 # sprintf
.data 0x00264EA0 # Original function for on_window_created callsite
.data 0x00000000
.binary "%s\n\nHP:%d/%d"
.data 0x00000000
.data 0x00000000
str_data_end:
.data 0x002DB080
.deltaof new_code_start, new_code_end
new_code_start:
.include EnemyHPBars-TextHandlerXB
new_code_end:
.data 0x0026B261
.data 0x00000007
nop
nop
.binary E81BFE0600 # call 002DB083 (on_hp_updated)
.data 0x0026B048
.data 0x00000005
.binary E834000700 # call 002DB081 (on_window_created)
.data 0x00000000
.data 0x00000000
@@ -12,30 +12,58 @@ start:
.include WriteCodeBlocksXB
.data 0x0026B193
.data 0x00000001
.binary A0
.binary C0
.data 0x0026B19C
.data 0x00000001
.binary DA
.binary FA
.data 0x0026B396
.data 0x00000004
.binary 836004FD
.data 0x0054ACCC
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054ACD8
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054ACE4
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054ACF0
.data 0x00000004
.data 0x42640000
.data 0x42960000
.data 0x0054AD8C
.data 0x00000004
.data 0x42300000
.data 0x42780000
.data 0x0054ADA8
.data 0x00000004
.data 0xFF00FF15
.data 0x00010C00
.deltaof str_data_start, str_data_end
str_data_start:
.data 0x00318858 # sprintf
.data 0x00264FD0 # Original function for on_window_created callsite
.data 0x00000000
.binary "%s\n\nHP:%d/%d"
.data 0x00000000
.data 0x00000000
str_data_end:
.data 0x002DB5D0
.deltaof new_code_start, new_code_end
new_code_start:
.include EnemyHPBars-TextHandlerXB
new_code_end:
.data 0x0026B371
.data 0x00000007
nop
nop
.binary E85B020700 # call 002DB5D3 (on_hp_updated)
.data 0x0026B158
.data 0x00000005
.binary E874040700 # call 002DB5D1 (on_window_created)
.data 0x00000000
.data 0x00000000
@@ -1,10 +1,6 @@
# This patch gives you the maximum number of each card. It only works if used
# in-game, which means it must be used by running `$patch AllCards`.
# This patch is only for PSO Episode 3 USA, which means it requires the
# EnableEpisode3SendFunctionCall option to be enabled in config.json. If that
# option is disabled, the Patches menu won't appear for the client.
.meta hide_from_patches_menu
.meta name="Get all cards"
.meta description="This patch gives you\nthe maximum number\nof each card."
@@ -4,13 +4,9 @@
# present in PSO PC and PSOX as well, but not in GC Episodes 1 & 2. There are
# notes in the below comments that may help get these editors working on PSO PC.
# This patch is only for PSO Episode 3 USA, which means it requires the
# EnableEpisode3SendFunctionCall option to be enabled in config.json. If that
# option is disabled, the Patches menu won't appear for the client. If this
# patch is run on a different client version, it will do nothing. Also, this
# patch must not be run from the Patches menu - it should only be run with the
# $patch command, since the client will likely crash if the player is not in a
# game or lobby when the patch runs.
# This patch must not be run from the Patches menu - it should only be run with
# the $patch command, since the client will likely crash if the player is not
# in a game or lobby when the patch runs.
.meta hide_from_patches_menu
.meta name="Editors"
@@ -1,8 +1,3 @@
# This patch is only for PSO Episode 3 USA, which means it requires the
# EnableEpisode3SendFunctionCall option to be enabled in config.json. If that
# option is disabled, the Patches menu won't appear for the client. If this
# patch is run on a different client version, it will do nothing.
.meta name="Get VIP card"
.meta description="Gives you a VIP card"
@@ -14,3 +14,4 @@ data:
.data 0x805C4D80 # root_protocol (anchor: send_05)
.data 0x803DB138 # free9
.data 0x800787B0 # TProtocol_wait_send_drain
.data 0x00002370 # sizeof(*char_file_part2)
@@ -14,3 +14,4 @@ data:
.data 0x805CBD60 # root_protocol (anchor: send_05)
.data 0x803DB190 # free9
.data 0x800787B0 # TProtocol_wait_send_drain
.data 0x00002370 # sizeof(*char_file_part2)
@@ -14,3 +14,4 @@ data:
.data 0x805D5580 # root_protocol (anchor: send_05)
.data 0x803DE890 # free9
.data 0x8007889C # TProtocol_wait_send_drain
.data 0x00002370 # sizeof(*char_file_part2)
@@ -14,3 +14,4 @@ data:
.data 0x805C4488 # root_protocol (anchor: send_05)
.data 0x803D9E90 # free9
.data 0x8007848C # TProtocol_wait_send_drain
.data 0x00002370 # sizeof(*char_file_part2)
@@ -14,3 +14,4 @@ data:
.data 0x805CEA50 # root_protocol (anchor: send_05)
.data 0x803DC870 # free9
.data 0x800785F0 # TProtocol_wait_send_drain
.data 0x00002370 # sizeof(*char_file_part2)
@@ -14,3 +14,4 @@ data:
.data 0x805D5ED0 # root_protocol (anchor: send_05)
.data 0x803DE710 # free9
.data 0x80078748 # TProtocol_wait_send_drain
.data 0x00002370 # sizeof(*char_file_part2)
@@ -14,3 +14,4 @@ data:
.data 0x805D5C70 # root_protocol (anchor: send_05)
.data 0x803DE4C0 # free9
.data 0x800786A0 # TProtocol_wait_send_drain
.data 0x00002370 # sizeof(*char_file_part2)
@@ -14,3 +14,4 @@ data:
.data 0x805D17C0 # root_protocol (anchor: send_05)
.data 0x803DD380 # free9
.data 0x80078820 # TProtocol_wait_send_drain
.data 0x00002370 # sizeof(*char_file_part2)
@@ -0,0 +1,17 @@
.meta hide_from_patches_menu
.meta name="GetExtendedPlayerInfo"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include GetExtendedPlayerInfoGC
data:
.data 0x8038C0EC # malloc9
.data 0x8057A6F0 # char_file_part1
.data 0x8057A6F4 # char_file_part2
.data 0x8057A150 # root_protocol (anchor: send_05)
.data 0x8038C144 # free9
.data 0x80026B88 # TProtocol_wait_send_drain
.data 0x0000358C # sizeof(*char_file_part2)
@@ -0,0 +1,17 @@
.meta hide_from_patches_menu
.meta name="GetExtendedPlayerInfo"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include GetExtendedPlayerInfoGC
data:
.data 0x8038B09C # malloc9
.data 0x80579880 # char_file_part1
.data 0x80579884 # char_file_part2
.data 0x805792E0 # root_protocol (anchor: send_05)
.data 0x8038B0F4 # free9
.data 0x80026A04 # TProtocol_wait_send_drain
.data 0x0000358C # sizeof(*char_file_part2)
@@ -0,0 +1,17 @@
.meta hide_from_patches_menu
.meta name="GetExtendedPlayerInfo"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include GetExtendedPlayerInfoGC
data:
.data 0x80358094 # malloc9
.data 0x8058B980 # char_file_part1
.data 0x8058B984 # char_file_part2
.data 0x8058B3A0 # root_protocol (anchor: send_05)
.data 0x803580EC # free9
.data 0x80026FE4 # TProtocol_wait_send_drain
.data 0x000041F4 # sizeof(*char_file_part2)
@@ -0,0 +1,17 @@
.meta hide_from_patches_menu
.meta name="GetExtendedPlayerInfo"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include GetExtendedPlayerInfoGC
data:
.data 0x8038CF94 # malloc9
.data 0x8057CB10 # char_file_part1
.data 0x8057CB14 # char_file_part2
.data 0x8057C570 # root_protocol (anchor: send_05)
.data 0x8038CFEC # free9
.data 0x80026BB8 # TProtocol_wait_send_drain
.data 0x0000358C # sizeof(*char_file_part2)
@@ -1,25 +1,31 @@
stwu [r1 - 0x20], r1
stwu [r1 - 0x40], r1
mflr r0
stw [r1 + 0x24], r0
stw [r1 + 0x44], r0
stw [r1 + 0x08], r31
stw [r1 + 0x0C], r30
stw [r1 + 0x10], r29
stw [r1 + 0x14], r28
stw [r1 + 0x18], r27
b get_data_ptr
get_data_ptr_ret:
mflr r30
li r3, 0x279C
lwz r27, [r30 + 0x18] # sizeof(part2)
addi r3, r27, 0x42C # sizeof(header) + sizeof(part1) + sizeof(part2) + sizeof(unused fields after part2)
lwz r0, [r30]
mtctr r0
bctrl # malloc9
mr. r31, r3
beq malloc9_failed
lis r0, 0x3000
ori r0, r0, 0x9C27
stw [r31], r0 # header = 30 00 9C 27
li r0, 0x3000
sth [r31], r0
addi r3, r27, 0x42C
stb [r31 + 2], r3
rlwinm r3, r3, 24, 24, 31
stb [r31 + 3], r3 # header = 30 00 SS SS
lwz r4, [r30 + 0x04]
lwz r4, [r4] # r4 = char_file_part1
@@ -30,16 +36,18 @@ get_data_ptr_ret:
lwz r4, [r30 + 0x08]
lwz r4, [r4] # r4 = char_file_part2
addi r3, r31, 0x0420
li r5, 0x2370 # sizeof(part2)
mr r5, r27
bl memcpy
li r0, 0
stw [r31 + 0x2790], r0
stw [r31 + 0x2794], r0
stw [r31 + 0x2798], r0
add r3, r27, r31
addi r3, r3, 0x420 # r3 = pointer to unused fields after part2
stw [r3 + 0], r0
stw [r3 + 4], r0
stw [r3 + 8], r0
mr r28, r31
li r29, 0x279C
addi r29, r27, 0x42C
send_again:
lwz r3, [r30 + 0x0C]
lwz r3, [r3]
@@ -74,12 +82,13 @@ drain_failed:
li r3, 0
malloc9_failed:
lwz r27, [r1 + 0x18]
lwz r28, [r1 + 0x14]
lwz r29, [r1 + 0x10]
lwz r30, [r1 + 0x0C]
lwz r31, [r1 + 0x08]
lwz r0, [r1 + 0x24]
addi r1, r1, 0x20
lwz r0, [r1 + 0x44]
addi r1, r1, 0x40
mtlr r0
blr
@@ -9,4 +9,5 @@ start:
.include SetExtendedPlayerInfoGC
data:
.data 0x805C5758 # character_file
.data 0x00002370 # sizeof(part2)
# Server adds a PSOGCCharacterFile::Character here
@@ -9,4 +9,5 @@ start:
.include SetExtendedPlayerInfoGC
data:
.data 0x805CC738 # character_file
.data 0x00002370 # sizeof(part2)
# Server adds a PSOGCCharacterFile::Character here
@@ -9,4 +9,5 @@ start:
.include SetExtendedPlayerInfoGC
data:
.data 0x805D5F58 # character_file
.data 0x00002370 # sizeof(part2)
# Server adds a PSOGCCharacterFile::Character here
@@ -9,4 +9,5 @@ start:
.include SetExtendedPlayerInfoGC
data:
.data 0x805C4E60 # character_file
.data 0x00002370 # sizeof(part2)
# Server adds a PSOGCCharacterFile::Character here
@@ -9,4 +9,5 @@ start:
.include SetExtendedPlayerInfoGC
data:
.data 0x805CF428 # character_file
.data 0x00002370 # sizeof(part2)
# Server adds a PSOGCCharacterFile::Character here
@@ -9,4 +9,5 @@ start:
.include SetExtendedPlayerInfoGC
data:
.data 0x805D68A8 # character_file
.data 0x00002370 # sizeof(part2)
# Server adds a PSOGCCharacterFile::Character here
@@ -9,4 +9,5 @@ start:
.include SetExtendedPlayerInfoGC
data:
.data 0x805D6648 # character_file
.data 0x00002370 # sizeof(part2)
# Server adds a PSOGCCharacterFile::Character here
@@ -6,44 +6,8 @@ entry_ptr:
reloc0:
.offsetof start
start:
mflr r12
bl get_data_ptr
get_data_ptr_ret:
mflr r11
lwz r10, [r11]
# Copy part1 data into place
lwz r3, [r10 + 0x08]
addi r4, r11, 0x0004
li r5, 0x41C
bl memcpy
# Copy part2 data into place, but retain the values of a few metadata fields
# so the game won't think the file is corrupt
lwz r3, [r10 + 0x0C]
lwz r7, [r3 + 0x04] # creation_timestamp
lwz r8, [r3 + 0x08] # signature
lwz r9, [r3 + 0x14] # save_count
addi r4, r11, 0x0420
li r5, 0x2268
bl memcpy
lwz r3, [r10 + 0x0C]
stw [r3 + 0x04], r7 # creation_timestamp
stw [r3 + 0x08], r8 # signature
stw [r3 + 0x14], r9 # save_count
li r3, 1
mtlr r12
blr
memcpy:
.include CopyDataWords
blr
get_data_ptr:
bl get_data_ptr_ret
.include SetExtendedPlayerInfoGC
data:
.data 0x805CC738 # character_file
# Server adds a PSOGCCharacterFile::Character here
.data 0x00002268 # sizeof(part2)
# Server adds a PSOGCNTECharacterFileCharacter here
@@ -9,4 +9,5 @@ start:
.include SetExtendedPlayerInfoGC
data:
.data 0x805D2198 # character_file
.data 0x00002370 # sizeof(part2)
# Server adds a PSOGCCharacterFile::Character here
@@ -0,0 +1,13 @@
.meta hide_from_patches_menu
.meta name="SetExtendedPlayerInfo"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include SetExtendedPlayerInfoGC
data:
.data 0x8057A6E8 # character_file
.data 0x0000358C # sizeof(*char_file_part2)
# Server adds a PSOGCEp3CharacterFile::Character here
@@ -0,0 +1,13 @@
.meta hide_from_patches_menu
.meta name="SetExtendedPlayerInfo"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include SetExtendedPlayerInfoGC
data:
.data 0x80579878 # character_file
.data 0x0000358C # sizeof(*char_file_part2)
# Server adds a PSOGCEp3CharacterFile::Character here
@@ -0,0 +1,13 @@
.meta hide_from_patches_menu
.meta name="SetExtendedPlayerInfo"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include SetExtendedPlayerInfoGC
data:
.data 0x8058B978 # character_file
.data 0x0000358C # sizeof(*char_file_part2)
# Server adds a PSOGCEp3CharacterFile::Character here
@@ -0,0 +1,13 @@
.meta hide_from_patches_menu
.meta name="SetExtendedPlayerInfo"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include SetExtendedPlayerInfoGC
data:
.data 0x8057CB08 # character_file
.data 0x0000358C # sizeof(*char_file_part2)
# Server adds a PSOGCEp3CharacterFile::Character here
@@ -7,7 +7,7 @@ get_data_ptr_ret:
# Copy part1 data into place
lwz r3, [r10 + 0x08]
addi r4, r11, 0x0004
addi r4, r11, 0x0008
li r5, 0x41C
bl memcpy
@@ -17,8 +17,8 @@ get_data_ptr_ret:
lwz r7, [r3 + 0x04] # creation_timestamp
lwz r8, [r3 + 0x08] # signature
lwz r9, [r3 + 0x14] # save_count
addi r4, r11, 0x0420
li r5, 0x2370
addi r4, r11, 0x0424
lwz r5, [r11 + 4]
bl memcpy
lwz r3, [r10 + 0x0C]
stw [r3 + 0x04], r7 # creation_timestamp
@@ -24,7 +24,7 @@ start:
and r0, 0xFE
.align 4
.data 0x8C160A4C
.data 0x8C160A4A
.data 2
and r0, 0xFE
@@ -24,7 +24,7 @@ start:
and r0, 0xFE
.align 4
.data 0x8C16053C
.data 0x8C16053A
.data 2
and r0, 0xFE
@@ -24,7 +24,7 @@ start:
and r0, 0xFE
.align 4
.data 0x8C160808
.data 0x8C160806
.data 2
and r0, 0xFE
@@ -13,6 +13,18 @@ start:
.data 0x801D33E4 # address
.data 0x00000004 # size
.data 0x4800004C # 801D33E4 => b +0x0000004C /* 801D3430 */
# region @ 801FE900 (4 bytes)
.data 0x801FE900 # address
.data 0x00000004 # size
.data 0x60000000 # 801FE900 => nop
# region @ 801FFE5C (4 bytes)
.data 0x801FFE5C # address
.data 0x00000004 # size
.data 0x60000000 # 801FFE5C => nop
# region @ 802019C8 (4 bytes)
.data 0x802019C8 # address
.data 0x00000004 # size
.data 0x38000000 # 802019C8 => li r0, 0x0000
# region @ 802C2060 (4 bytes)
.data 0x802C2060 # address
.data 0x00000004 # size

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