From 4c248c5ee53abb10276f905fbdd27b5c99980ec7 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Tue, 28 Mar 2023 09:22:31 -0700 Subject: [PATCH] fix notes on 44/A6 commands --- src/CommandFormats.hh | 46 +++++++++++++++++++++++-------------------- src/Quest.cc | 2 +- src/SendCommands.cc | 9 ++++----- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 7c577276..17f39909 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -923,23 +923,35 @@ struct S_GuildCardSearchResult_BB_41 // 43: Invalid command // 44 (S->C): Open file for download -// Used for downloading online quests. For download quests (to be saved to the -// memory card), use A6 instead. -// Unlike the A6 command, the client will react to a 44 command only if the -// filename ends in .bin or .dat. +// Used for downloading online quests. The client will react to a 44 command if +// the filename ends in .bin or .dat. +// 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). +// It appears that the .gba handler for A6 was not deleted in PSO XB, even +// though it doesn't make sense for an XB client to receive such a file. struct S_OpenFile_DC_44_A6 { - ptext name; // Should begin with "PSO/" - parray unused; - uint8_t flags = 0; + ptext name; // Should begin with "PSO/" + // The type field is only used for download quests (A6); it is ignored for + // online quests (44). The following values are valid for A6: + // 0 = download quest (client expects .bin and .dat files) + // 1 = download quest (client expects .bin, .dat, and .pvr files) + // 2 = GBA game (GC only; client expects .gba file only) + // 3 = Episode 3 download quest (Ep3 only; client expects .bin file only) + // There is a bug in the type logic: an A6 command always overwrites the + // current download type even if the filename doesn't end in .bin, .dat, .pvr, + // or .gba. This may lead to a resource exhaustion bug if exploited carefully, + // but I haven't verified this. Generally the server should send all files for + // a given piece of content with the same type in each file's A6 command. + uint8_t type = 0; ptext filename; le_uint32_t file_size = 0; } __packed__; struct S_OpenFile_PC_V3_44_A6 { - ptext name; // Should begin with "PSO/" - parray unused; - le_uint16_t flags = 0; // 0 = download quest, 2 = online quest, 3 = Episode 3 + ptext name; // Should begin with "PSO/" + le_uint16_t type = 0; ptext filename; le_uint32_t file_size = 0; } __packed__; @@ -953,7 +965,7 @@ struct S_OpenFile_XB_44_A6 : S_OpenFile_PC_V3_44_A6 { struct S_OpenFile_BB_44_A6 { parray unused; - le_uint16_t flags = 0; + le_uint16_t type = 0; ptext filename; le_uint32_t file_size = 0; ptext name; @@ -1776,16 +1788,8 @@ struct S_QuestMenuEntry_BB_A2_A4 : S_QuestMenuEntry { } __packed // Same format as 1A/D5 command (plain text) // A6: Open file for download -// Same format as 44. -// Used for download quests and GBA games. The client will react to this command -// if the filename ends in .bin/.dat (download quests), .gba (GameBoy Advance -// games), and, curiously, .pvr (textures). To my knowledge, the .pvr handler in -// this command has never been used. -// It also appears that the .gba handler was not deleted in PSO XB, even though -// it doesn't make sense for an XB client to receive such a file. -// For .bin files, the flags field should be zero. For .pvr files, the flags -// field should be 1. For .dat and .gba files, it seems the value in the flags -// field does not matter. +// Same format as 44. See the description of 44 for some notes on the +// differences between the two commands. // Like the 44 command, the client->server form of this command is only used on // V3 and BB. diff --git a/src/Quest.cc b/src/Quest.cc index 05da74b2..816dbc46 100644 --- a/src/Quest.cc +++ b/src/Quest.cc @@ -1000,7 +1000,7 @@ void add_open_file_command(StringWriter& w, const Quest& q, bool is_bin) { CmdT cmd; cmd.name = "PSO/" + encode_sjis(q.name); cmd.filename = q.file_basename + (is_bin ? ".bin" : ".dat"); - cmd.flags = q.is_dlq_encoded ? 0 : 2; + cmd.type = 0; // TODO: It'd be nice to have something like w.emplace(...) to avoid copying // the command structs into the StringWriter. w.put(cmd); diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 946876a1..ef60623e 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -270,27 +270,26 @@ void send_quest_open_file_t( case QuestFileType::ONLINE: command_num = 0x44; cmd.name = "PSO/" + quest_name; - cmd.flags = 2; + cmd.type = 0; break; case QuestFileType::GBA_DEMO: command_num = 0xA6; cmd.name = "GBA Demo"; - cmd.flags = 2; + cmd.type = 2; break; case QuestFileType::DOWNLOAD: command_num = 0xA6; cmd.name = "PSO/" + quest_name; - cmd.flags = 0; + cmd.type = 0; break; case QuestFileType::EPISODE_3: command_num = 0xA6; cmd.name = "PSO/" + quest_name; - cmd.flags = 3; + cmd.type = 3; break; default: throw logic_error("invalid quest file type"); } - cmd.unused.clear(0); cmd.file_size = file_size; cmd.filename = filename.c_str(); send_command_t(c, command_num, 0x00, cmd);