Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ea3d0ad4b | |||
| 90efde7aa9 | |||
| 55f1869125 | |||
| b4efd90fdc | |||
| 87dd554592 | |||
| 58974ae1be | |||
| 21c8bab91c | |||
| c58b37be23 | |||
| d3d98c44b8 | |||
| dc2e73d198 | |||
| 774f9649da | |||
| 093287af75 | |||
| 0126189cbd | |||
| c250a2dbc4 | |||
| 2ff9df19c8 | |||
| 528593651b | |||
| 9f073d07cd | |||
| 4bd6ef12a9 | |||
| 52644695a3 | |||
| 45e619718c | |||
| 43fd979763 | |||
| 082bc49a4d | |||
| 4adcaa7bee | |||
| 630ae0beb4 | |||
| 246dfd9fe0 | |||
| 6f056cb1bd | |||
| 9322c023da | |||
| fd4719f8ec | |||
| 3a22a5c489 | |||
| 862b3d27da | |||
| 998664d2fb | |||
| 0bf2d950ac | |||
| 3ae5e875a1 | |||
| a88795d8b9 | |||
| 9ca1b79409 | |||
| ce8277b96a | |||
| 25731eb71f | |||
| e55963b82b | |||
| b9d9b38351 | |||
| 782babf3ae | |||
| 9869fa03c2 | |||
| 0ae02b0191 | |||
| c0ea976fdc | |||
| c4bf9e7d5b | |||
| 2e5d95d612 | |||
| 75b2827da9 |
@@ -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,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.)
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+41
-11
@@ -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
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable → Regular
Binary file not shown.
Executable → Regular
+20
-10
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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") {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user