add untested support for JP Plus/Ep3 send_function_call
This commit is contained in:
+7
-3
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user