add untested support for JP Plus/Ep3 send_function_call

This commit is contained in:
Martin Michelsen
2022-07-21 00:59:21 -07:00
parent e55cf3bc7c
commit c3ccd74e80
5 changed files with 55 additions and 16 deletions
+7 -3
View File
@@ -41,19 +41,23 @@ struct Client {
IN_INFORMATION_MENU = 0x0080,
// Client is at the welcome message (login server only)
AT_WELCOME_MESSAGE = 0x0100,
// Client disconnect if it receives B2 (send_function_call)
// Client disconnects if it receives B2 (send_function_call)
DOES_NOT_SUPPORT_SEND_FUNCTION_CALL = 0x0200,
// Client has already received a 97 (enable saves) command, so don't show
// the programs menu anymore
SAVE_ENABLED = 0x0400,
// Client requires doubly-encrypted code section in send_function_call
ENCRYPTED_SEND_FUNCTION_CALL = 0x0800,
// TODO: Do DCv1 and PC support send_function_call? Here we assume they don't
DEFAULT_V1 = DCV1 | NO_MESSAGE_BOX_CLOSE_CONFIRMATION | DOES_NOT_SUPPORT_SEND_FUNCTION_CALL,
DEFAULT_V2_DC = NO_MESSAGE_BOX_CLOSE_CONFIRMATION,
DEFAULT_V2_PC = NO_MESSAGE_BOX_CLOSE_CONFIRMATION | DOES_NOT_SUPPORT_SEND_FUNCTION_CALL,
DEFAULT_V3_GC = 0x0000,
DEFAULT_V3_GC_PLUS = NO_MESSAGE_BOX_CLOSE_CONFIRMATION_AFTER_LOBBY_JOIN | DOES_NOT_SUPPORT_SEND_FUNCTION_CALL,
DEFAULT_V3_GC_EP3 = NO_MESSAGE_BOX_CLOSE_CONFIRMATION_AFTER_LOBBY_JOIN | EPISODE_3 | DOES_NOT_SUPPORT_SEND_FUNCTION_CALL,
DEFAULT_V3_GC_PLUS = NO_MESSAGE_BOX_CLOSE_CONFIRMATION_AFTER_LOBBY_JOIN | ENCRYPTED_SEND_FUNCTION_CALL,
DEFAULT_V3_GC_PLUS_NO_SFC = DEFAULT_V3_GC_PLUS | DOES_NOT_SUPPORT_SEND_FUNCTION_CALL,
DEFAULT_V3_GC_EP3 = NO_MESSAGE_BOX_CLOSE_CONFIRMATION_AFTER_LOBBY_JOIN | EPISODE_3 | ENCRYPTED_SEND_FUNCTION_CALL,
DEFAULT_V3_GC_EP3_NO_SFC = DEFAULT_V3_GC_EP3 | DOES_NOT_SUPPORT_SEND_FUNCTION_CALL,
DEFAULT_V4_BB = NO_MESSAGE_BOX_CLOSE_CONFIRMATION_AFTER_LOBBY_JOIN | NO_MESSAGE_BOX_CLOSE_CONFIRMATION | SAVE_ENABLED,
};
+11 -6
View File
@@ -1383,12 +1383,17 @@ struct S_ConfirmUpdateQuestStatistics_AB {
// B2 (S->C): Execute code and/or checksum memory
// Client will respond with a B3 command with the same header.flag value as was
// sent in the B2.
// This command doesn't work on PSO Plus (v1.2) or Episode 3. Sega presumably
// removed it after taking heat from Nintendo about enabling homebrew on the
// GameCube. On PSO PC, the code section (if included in the B2 command) is
// parsed and relocated, but is not actually executed, so the return_value field
// in the resulting B3 command is always 0. The checksum functionality does work
// on PSO PC, just like the other versions.
// On PSO PC, the code section (if included in the B2 command) is parsed and
// relocated, but is not actually executed, so the return_value field in the
// resulting B3 command is always 0. The checksum functionality does work on PSO
// PC, just like the other versions.
// This command doesn't work on the later JP PSO Plus (v1.5?), US PSO Plus
// (v1.2), or US Episode 3. Sega presumably removed it after taking heat from
// Nintendo about enabling homebrew on the GameCube. On the earlier JP PSO Plus
// (v1.4) and JP Episode 3, this command is implemented as described here, with
// some additional compression and encryption steps added, similarly to how
// download quests are encoded. See send_function_call in SendCommands.cc for
// more details on how this works.
struct S_ExecuteCode_B2 {
// If code_size == 0, no code is executed, but checksumming may still occur.
+4 -2
View File
@@ -40,9 +40,10 @@ public:
virtual void encrypt(void* data, size_t size, bool advance = true);
uint32_t next(bool advance = true);
protected:
void update_stream();
uint32_t next(bool advance = true);
uint32_t stream[PC_STREAM_LENGTH + 1];
uint8_t offset;
@@ -54,9 +55,10 @@ public:
virtual void encrypt(void* data, size_t size, bool advance = true);
uint32_t next(bool advance = true);
protected:
void update_stream();
uint32_t next(bool advance = true);
uint32_t stream[GC_STREAM_LENGTH];
uint16_t offset;
+26 -2
View File
@@ -13,6 +13,7 @@
#include "PSOProtocol.hh"
#include "CommandFormats.hh"
#include "Compression.hh"
#include "FileContentsCache.hh"
#include "Text.hh"
@@ -225,8 +226,8 @@ void send_function_call(
if (c->version != GameVersion::GC) {
throw logic_error("cannot send function calls to non-GameCube clients");
}
if (c->flags & Client::Flag::EPISODE_3) {
throw logic_error("cannot send function calls to Episode 3 clients");
if (c->flags & Client::Flag::DOES_NOT_SUPPORT_SEND_FUNCTION_CALL) {
throw logic_error("client does not support function calls");
}
string data;
@@ -236,6 +237,29 @@ void send_function_call(
index = code->index;
}
if (c->flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
uint32_t key = random_object<uint32_t>();
StringWriter w;
w.put_u32b(data.size());
w.put_u32b(key);
// Round size up to a multiple of 4 for encryption
data.resize(data.size() + 3 & ~3);
// For this format, the code section is decrypted without byteswapping on
// the client (GameCube), which is big-endian, so we have to treat the data
// the same way here (hence we can't just use crypt.encrypt).
data = prs_compress(data);
StringReader compressed_r(data);
PSOPCEncryption crypt(key);
while (!compressed_r.eof()) {
w.put_u32b(compressed_r.get_u32b() ^ crypt.next());
}
data = move(w.str());
}
S_ExecuteCode_B2 header = {data.size(), checksum_addr, checksum_size};
StringWriter w;
+7 -3
View File
@@ -33,16 +33,20 @@ uint16_t flags_for_version(GameVersion version, uint8_t sub_version) {
case 0x33: // PSO Ep1&2 EU50HZ
case 0x34: // PSO Ep1&2 JP11
return Client::Flag::DEFAULT_V3_GC;
// TODO: Which of these is the first version of JP PSO Plus? That version
// supports encrypted send_function_call; we should set the appropriate flag
// here.
case 0x32: // PSO Ep1&2 US12, JP12
case 0x35: // PSO Ep1&2 US12, JP12
case 0x36: // PSO Ep1&2 US12, JP12
case 0x39: // PSO Ep1&2 US12, JP12
return Client::Flag::DEFAULT_V3_GC_PLUS;
return Client::Flag::DEFAULT_V3_GC_PLUS_NO_SFC;
case 0x40: // PSO Ep3 trial
case 0x41: // PSO Ep3 US
case 0x42: // PSO Ep3 JP
case 0x43: // PSO Ep3 UK
return Client::Flag::DEFAULT_V3_GC_EP3;
return Client::Flag::DEFAULT_V3_GC_EP3_NO_SFC;
case 0x42: // PSO Ep3 JP
return Client::Flag::DEFAULT_V3_GC_PLUS;
}
return 0;
}