add some Ep3 command notes
This commit is contained in:
+24
-11
@@ -4069,8 +4069,10 @@ struct G_SymbolChat_6x07 {
|
||||
// equal to 0x1000, so any valid enemy ID would be far outside the array's
|
||||
// range. newserv unconditionally blocks this command because it appears never
|
||||
// to be used, and the array write is not bounds-checked, so it could be used
|
||||
// to cause undefined behavior on other clients. It seems that this broken
|
||||
// logic predates even DC NTE.
|
||||
// to cause undefined behavior on other clients. It seems that this logic
|
||||
// predates even DC NTE; it's likely that this was part of the implementation
|
||||
// of enemy states before entity IDs and the standard 0xB50-entry array of
|
||||
// states were introduced.
|
||||
|
||||
struct G_LegacyKillEnemy_6x09 {
|
||||
G_EntityIDHeader header;
|
||||
@@ -4455,11 +4457,11 @@ struct G_UseMedicalCenter_6x31 {
|
||||
G_ClientIDHeader header;
|
||||
} __packed_ws__(G_UseMedicalCenter_6x31, 4);
|
||||
|
||||
// 6x32: Revive player (Medical Center)
|
||||
// 6x32: Revive all players (Medical Center)
|
||||
|
||||
struct G_MedicalCenterRevivePlayer_6x32 {
|
||||
struct G_MedicalCenterReviveAllPlayers_6x32 {
|
||||
G_UnusedHeader header;
|
||||
} __packed_ws__(G_MedicalCenterRevivePlayer_6x32, 4);
|
||||
} __packed_ws__(G_MedicalCenterReviveAllPlayers_6x32, 4);
|
||||
|
||||
// 6x33: Revive player (with Moon Atomizer) (protected on V3/V4)
|
||||
|
||||
@@ -4692,7 +4694,7 @@ struct G_PlayerDied_6x4D {
|
||||
le_uint32_t death_flags = 0; // Same as 6x70's death_flags field
|
||||
} __packed_ws__(G_PlayerDied_6x4D, 8);
|
||||
|
||||
// 6x4E: Player is dead can be revived (protected on GC NTE/V3/V4)
|
||||
// 6x4E: Player can be revived (protected on GC NTE/V3/V4)
|
||||
// This command creates the particle effect that Reverser and Moon Atomizers
|
||||
// can target.
|
||||
|
||||
@@ -4707,7 +4709,9 @@ 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.
|
||||
// If UDP mode is enabled, this command is sent via UDP. This command doesn't
|
||||
// actually do anything with the switch; it just sets the player's animation
|
||||
// state. 6x05 is used to set the switch flag if needed.
|
||||
|
||||
struct G_SwitchInteraction_6x50 {
|
||||
G_ClientIDHeader header;
|
||||
@@ -4804,12 +4808,11 @@ struct G_PickUpItemRequest_6x5A {
|
||||
// This command has a handler, but it does nothing, even on DC NTE.
|
||||
|
||||
// 6x5C: Destroy floor item
|
||||
// Same format as 6x63. It appears this version should not be used because it
|
||||
// Same format as 6x63. It appears this command should not be used because it
|
||||
// removes the item from the floor just like 6x63 does, but 6x5C doesn't call
|
||||
// the item's destructor.
|
||||
|
||||
// 6x5D: Drop meseta or stacked item
|
||||
// On DC NTE, this command has the same format, but is subcommand 6x4F instead.
|
||||
|
||||
struct G_DropStackedItem_DC_6x5D {
|
||||
G_ClientIDHeader header;
|
||||
@@ -4898,10 +4901,10 @@ struct G_DestroyFloorItem_6x5C_6x63 {
|
||||
} __packed_ws__(G_DestroyFloorItem_6x5C_6x63, 0x0C);
|
||||
|
||||
// 6x64: Unused (not valid on Episode 3)
|
||||
// This command has a handler, but it does nothing even on DC NTE.
|
||||
// This command has a handler, but it does nothing, even on DC NTE.
|
||||
|
||||
// 6x65: Unused (not valid on Episode 3)
|
||||
// This command has a handler, but it does nothing even on DC NTE.
|
||||
// This command has a handler, but it does nothing, even on DC NTE.
|
||||
|
||||
// 6x66: Use star atomizer
|
||||
|
||||
@@ -5057,6 +5060,9 @@ struct G_SyncSetFlagState_6x6E_Decompressed {
|
||||
} __packed_ws__(G_SyncSetFlagState_6x6E_Decompressed, 8);
|
||||
|
||||
// 6x6F: Set quest flags (used while loading into game)
|
||||
// On Episode 3, this command sets the seq vars instead. However, the client
|
||||
// never sends this, since seq vars don't need to be synced to the entire game
|
||||
// in online play.
|
||||
|
||||
struct G_SetQuestFlags_DCv1_6x6F {
|
||||
G_UnusedHeader header;
|
||||
@@ -5068,6 +5074,11 @@ struct G_SetQuestFlags_V2_V3_6x6F {
|
||||
QuestFlags quest_flags;
|
||||
} __packed_ws__(G_SetQuestFlags_V2_V3_6x6F, 0x204);
|
||||
|
||||
struct G_SetSeqVars_Ep3_6x6F {
|
||||
G_UnusedHeader header;
|
||||
Ep3SeqVars seq_vars;
|
||||
} __packed_ws__(G_SetSeqVars_Ep3_6x6F, 0x404);
|
||||
|
||||
struct G_SetQuestFlags_BB_6x6F {
|
||||
G_UnusedHeader header;
|
||||
QuestFlags quest_flags;
|
||||
@@ -5248,6 +5259,7 @@ check_struct_size(G_WordSelect_6x74, 0x20);
|
||||
check_struct_size(G_WordSelectBE_6x74, 0x20);
|
||||
|
||||
// 6x75: Update quest flag
|
||||
// This command does nothing on Episode 3.
|
||||
|
||||
struct G_UpdateQuestFlag_DC_PC_6x75 {
|
||||
G_UnusedHeader header;
|
||||
@@ -5272,6 +5284,7 @@ struct G_SetEntitySetFlags_6x76 {
|
||||
|
||||
// 6x77: Sync quest register
|
||||
// This is sent by the client when an opcode D9 is executed within a quest.
|
||||
// This command does nothing on Episode 3.
|
||||
|
||||
struct G_SyncQuestRegister_6x77 {
|
||||
G_UnusedHeader header;
|
||||
|
||||
@@ -309,22 +309,7 @@ PlayerRecordsChallengeBB::operator PlayerRecordsChallengePC() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
QuestFlagsV1& QuestFlagsV1::operator=(const QuestFlags& other) {
|
||||
this->data[0] = other.data[0];
|
||||
this->data[1] = other.data[1];
|
||||
this->data[2] = other.data[2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuestFlagsV1::operator QuestFlags() const {
|
||||
QuestFlags ret;
|
||||
ret.data[0] = this->data[0];
|
||||
ret.data[1] = this->data[1];
|
||||
ret.data[2] = this->data[2];
|
||||
return ret;
|
||||
}
|
||||
|
||||
const QuestFlagsForDifficulty QuestFlagsForDifficulty::BB_QUEST_FLAG_APPLY_MASK{{
|
||||
const QuestFlagsForDifficulty BB_QUEST_FLAG_APPLY_MASK{{
|
||||
// clang-format off
|
||||
/* 0000 */ 0x00, 0x3F, 0xFF, 0xE3, 0xE0, 0xFF, 0xFF, 0x00,
|
||||
/* 0040 */ 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00,
|
||||
|
||||
+66
-41
@@ -884,10 +884,25 @@ inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB>(
|
||||
return src;
|
||||
}
|
||||
|
||||
struct QuestFlagsForDifficulty {
|
||||
static const QuestFlagsForDifficulty BB_QUEST_FLAG_APPLY_MASK;
|
||||
template <size_t NumFlags>
|
||||
struct FlagsArray {
|
||||
parray<uint8_t, (NumFlags >> 3)> data = 0;
|
||||
|
||||
parray<uint8_t, 0x80> data;
|
||||
FlagsArray() = default;
|
||||
FlagsArray(const FlagsArray& other) = default;
|
||||
FlagsArray(FlagsArray&& other) = default;
|
||||
FlagsArray& operator=(const FlagsArray& other) = default;
|
||||
FlagsArray& operator=(FlagsArray&& other) = default;
|
||||
|
||||
FlagsArray(std::initializer_list<uint8_t> init_items) : data(init_items) {}
|
||||
|
||||
template <size_t OtherNumFlags>
|
||||
explicit FlagsArray(const FlagsArray<OtherNumFlags>& other) : data(other.data) {}
|
||||
template <size_t OtherNumFlags>
|
||||
FlagsArray& operator=(const FlagsArray<OtherNumFlags>& other) {
|
||||
this->data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool get(uint16_t flag_index) const {
|
||||
size_t byte_index = flag_index >> 3;
|
||||
@@ -904,6 +919,7 @@ struct QuestFlagsForDifficulty {
|
||||
uint8_t mask = 0x80 >> (flag_index & 7);
|
||||
this->data[byte_index] &= (~mask);
|
||||
}
|
||||
|
||||
inline void update_all(bool set) {
|
||||
if (set) {
|
||||
this->data.clear(0xFF);
|
||||
@@ -911,57 +927,66 @@ struct QuestFlagsForDifficulty {
|
||||
this->data.clear(0x00);
|
||||
}
|
||||
}
|
||||
} __packed_ws__(QuestFlagsForDifficulty, 0x80);
|
||||
} __attribute__((packed));
|
||||
|
||||
struct QuestFlags {
|
||||
parray<QuestFlagsForDifficulty, 4> data;
|
||||
template <size_t NumFlagsPerTable, size_t NumTables, typename TableIndexT = size_t>
|
||||
struct FlagsTable {
|
||||
parray<FlagsArray<NumFlagsPerTable>, NumTables> data;
|
||||
|
||||
inline QuestFlagsForDifficulty& for_difficulty(Difficulty difficulty) {
|
||||
return this->data[static_cast<size_t>(difficulty)];
|
||||
}
|
||||
inline const QuestFlagsForDifficulty& for_difficulty(Difficulty difficulty) const {
|
||||
return this->data[static_cast<size_t>(difficulty)];
|
||||
FlagsTable() = default;
|
||||
FlagsTable(const FlagsTable& other) = default;
|
||||
FlagsTable(FlagsTable&& other) = default;
|
||||
FlagsTable& operator=(const FlagsTable& other) = default;
|
||||
FlagsTable& operator=(FlagsTable&& other) = default;
|
||||
|
||||
template <size_t OtherNumFlagsPerTable, size_t OtherNumTables>
|
||||
explicit FlagsTable(const FlagsTable<OtherNumFlagsPerTable, OtherNumTables, TableIndexT>& other) : data(other.data) {}
|
||||
template <size_t OtherNumFlagsPerTable, size_t OtherNumTables>
|
||||
FlagsTable& operator=(const FlagsTable<OtherNumFlagsPerTable, OtherNumTables, TableIndexT>& other) {
|
||||
this->data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool get(Difficulty difficulty, uint16_t flag_index) const {
|
||||
return this->for_difficulty(difficulty).get(flag_index);
|
||||
inline FlagsArray<NumFlagsPerTable>& array(TableIndexT which) {
|
||||
return this->data[static_cast<size_t>(which)];
|
||||
}
|
||||
inline void set(Difficulty difficulty, uint16_t flag_index) {
|
||||
this->for_difficulty(difficulty).set(flag_index);
|
||||
inline const FlagsArray<NumFlagsPerTable>& array(TableIndexT which) const {
|
||||
return this->data[static_cast<size_t>(which)];
|
||||
}
|
||||
inline void clear(Difficulty difficulty, uint16_t flag_index) {
|
||||
this->for_difficulty(difficulty).clear(flag_index);
|
||||
|
||||
inline bool get(TableIndexT array_index, size_t flag_index) const {
|
||||
return this->array(array_index).get(flag_index);
|
||||
}
|
||||
inline void update_all(Difficulty difficulty, bool set) {
|
||||
this->for_difficulty(difficulty).update_all(set);
|
||||
inline void set(TableIndexT array_index, size_t flag_index) {
|
||||
this->array(array_index).set(flag_index);
|
||||
}
|
||||
inline void clear(TableIndexT array_index, size_t flag_index) {
|
||||
this->array(array_index).clear(flag_index);
|
||||
}
|
||||
inline void update_all(TableIndexT array_index, bool set) {
|
||||
this->array(array_index).update_all(set);
|
||||
}
|
||||
inline void update_all(bool set) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
this->update_all(difficulty, set);
|
||||
for (size_t z = 0; z < this->data.size(); z++) {
|
||||
this->update_all(z, set);
|
||||
}
|
||||
}
|
||||
} __packed_ws__(QuestFlags, 0x200);
|
||||
} __attribute__((packed));
|
||||
|
||||
struct QuestFlagsV1 {
|
||||
parray<QuestFlagsForDifficulty, 3> data;
|
||||
using QuestFlagsForDifficulty = FlagsArray<0x400>;
|
||||
using QuestFlagsV1 = FlagsTable<0x400, 3, Difficulty>;
|
||||
using QuestFlags = FlagsTable<0x400, 4, Difficulty>;
|
||||
using Ep3SeqVars = FlagsArray<0x2000>;
|
||||
using SwitchFlagsV1 = FlagsTable<0x100, 0x10>;
|
||||
using SwitchFlags = FlagsTable<0x100, 0x12>;
|
||||
static_assert(sizeof(QuestFlagsForDifficulty) == 0x80);
|
||||
static_assert(sizeof(QuestFlagsV1) == 0x180);
|
||||
static_assert(sizeof(QuestFlags) == 0x200);
|
||||
static_assert(sizeof(Ep3SeqVars) == 0x400);
|
||||
static_assert(sizeof(SwitchFlagsV1) == 0x200);
|
||||
static_assert(sizeof(SwitchFlags) == 0x240);
|
||||
|
||||
QuestFlagsV1& operator=(const QuestFlags& other);
|
||||
operator QuestFlags() const;
|
||||
} __packed_ws__(QuestFlagsV1, 0x180);
|
||||
|
||||
struct SwitchFlags {
|
||||
parray<parray<uint8_t, 0x20>, 0x12> data;
|
||||
|
||||
inline bool get(uint8_t floor, uint16_t flag_num) const {
|
||||
return this->data[floor][flag_num >> 3] & (0x80 >> (flag_num & 7));
|
||||
}
|
||||
inline void set(uint8_t floor, uint16_t flag_num) {
|
||||
this->data[floor][flag_num >> 3] |= (0x80 >> (flag_num & 7));
|
||||
}
|
||||
inline void clear(uint8_t floor, uint16_t flag_num) {
|
||||
this->data[floor][flag_num >> 3] &= ~(0x80 >> (flag_num & 7));
|
||||
}
|
||||
} __packed_ws__(SwitchFlags, 0x240);
|
||||
extern const QuestFlagsForDifficulty BB_QUEST_FLAG_APPLY_MASK;
|
||||
|
||||
struct BattleRules {
|
||||
enum class TechDiskMode : uint8_t {
|
||||
|
||||
@@ -4737,7 +4737,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
|
||||
if (quest_flag_rewrites && !quest_flag_rewrites->empty()) {
|
||||
IntegralExpression::Env env = {
|
||||
.flags = &p->quest_flags.for_difficulty(difficulty),
|
||||
.flags = &p->quest_flags.array(difficulty),
|
||||
.challenge_records = &p->challenge_records,
|
||||
.team = creator_c->team(),
|
||||
.num_players = 1,
|
||||
|
||||
@@ -694,7 +694,7 @@ static asio::awaitable<void> on_sync_joining_player_compressed_state(shared_ptr<
|
||||
// player), it's not surprising that no one noticed this. But it does
|
||||
// mean we have to check switch_flags_r.eof() here.
|
||||
for (size_t z = 0; (z < 0x20) && !switch_flags_r.eof(); z++) {
|
||||
uint8_t& l_flags = l->switch_flags->data[floor][z];
|
||||
uint8_t& l_flags = l->switch_flags->array(floor).data[z];
|
||||
uint8_t r_flags = switch_flags_r.get_u8();
|
||||
if (l_flags != r_flags) {
|
||||
l->log.warning_f("Switch flags do not match at floor {:02X} byte {:02X} (expected {:02X}, received {:02X})",
|
||||
|
||||
@@ -681,7 +681,7 @@ struct PSOGCEp3NTECharacter {
|
||||
/* 0430:0014 */ be_uint32_t save_count = 1;
|
||||
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
|
||||
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
|
||||
/* 0460:0044 */ parray<uint8_t, 0x400> seq_vars;
|
||||
/* 0460:0044 */ Ep3SeqVars seq_vars;
|
||||
/* 0860:0444 */ be_uint32_t death_count = 0;
|
||||
/* 0864:0448 */ PlayerBank200BE bank;
|
||||
/* 1B2C:1710 */ GuildCardGCBE guild_card;
|
||||
@@ -722,7 +722,7 @@ struct PSOGCEp3CharacterFile {
|
||||
// NPC decks are unlocked, and whether the player has a VIP card or not.
|
||||
// Logically, this structure maps to quest_flags in other versions, but is
|
||||
// a different size.
|
||||
/* 0460:0044 */ parray<uint8_t, 0x400> seq_vars;
|
||||
/* 0460:0044 */ Ep3SeqVars seq_vars;
|
||||
/* 0860:0444 */ be_uint32_t death_count = 0;
|
||||
// Curiously, Episode 3 characters do have item banks, but there are only 4
|
||||
// item slots. Presumably Sega didn't completely remove the bank in Ep3
|
||||
|
||||
+2
-2
@@ -2956,8 +2956,8 @@ void send_game_flag_state_t(shared_ptr<Client> c) {
|
||||
if ((difficulty != l->difficulty) && !use_v3_cmd) {
|
||||
continue;
|
||||
}
|
||||
const auto& diff_flags = l->quest_flag_values->for_difficulty(difficulty);
|
||||
const auto& diff_known_flags = l->quest_flags_known->for_difficulty(difficulty);
|
||||
const auto& diff_flags = l->quest_flag_values->array(difficulty);
|
||||
const auto& diff_known_flags = l->quest_flags_known->array(difficulty);
|
||||
for (uint8_t z = 0; z < diff_known_flags.data.size(); z++) {
|
||||
uint8_t known_flags = diff_known_flags.data[z];
|
||||
if (!known_flags) {
|
||||
|
||||
+5
-1
@@ -243,7 +243,11 @@ struct parray {
|
||||
this->items[x] = s.items[x];
|
||||
}
|
||||
for (; x < Count; x++) {
|
||||
this->items[x] = 0;
|
||||
if constexpr (std::is_integral_v<ItemT>) {
|
||||
this->items[x] = 0;
|
||||
} else {
|
||||
this->items[x] = ItemT();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t x = 0; x < Count; x++) {
|
||||
|
||||
Reference in New Issue
Block a user