From 0c9d4bf3381cbd9eccd956215c6e23b612c0313b Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Fri, 25 Oct 2024 22:32:31 -0700 Subject: [PATCH] refine validation_flags in save file formats --- src/QuestScript.cc | 3 ++- src/SaveFileFormats.cc | 13 +++++++++++-- src/SaveFileFormats.hh | 39 ++++++++++++++++++++++++--------------- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/QuestScript.cc b/src/QuestScript.cc index 6f77447c..099b1f24 100644 --- a/src/QuestScript.cc +++ b/src/QuestScript.cc @@ -1948,7 +1948,8 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // 0x04 = unknown (TODO) (always zero on v3+) // 0x08 = hacked item flag has been set (see implementation of // are_rare_drops_allowed in ItemCreator.cc) - // 0x10 = any bits in PSOGCCharacterFile::Character::flags are set + // 0x10 = any bits in validation_flags in the character file are set (see + // PSOGCCharacterFile::Character in SaveFileFormats.hh) {0xF8B6, "check_for_hacking", "is_mag_hacked", {REG}, F_V2_V4}, // Challenge mode cannot be completed unless this many seconds have passed diff --git a/src/SaveFileFormats.cc b/src/SaveFileFormats.cc index 1e5b6587..9b8837d7 100644 --- a/src/SaveFileFormats.cc +++ b/src/SaveFileFormats.cc @@ -260,7 +260,7 @@ phosg::Image PSOGCSnapshotFile::decode_image() const { PSOGCEp3CharacterFile::Character::Character(const PSOGCEp3NTECharacter& nte) : inventory(nte.inventory), disp(nte.disp), - flags(nte.flags), + validation_flags(nte.validation_flags), creation_timestamp(nte.creation_timestamp), signature(nte.signature), play_time_seconds(nte.play_time_seconds), @@ -290,7 +290,7 @@ PSOGCEp3CharacterFile::Character::operator PSOGCEp3NTECharacter() const { PSOGCEp3NTECharacter ret; ret.inventory = this->inventory; ret.disp = this->disp; - ret.flags = this->flags; + ret.validation_flags = this->validation_flags; ret.creation_timestamp = this->creation_timestamp; ret.signature = this->signature; ret.play_time_seconds = this->play_time_seconds; @@ -549,6 +549,7 @@ shared_ptr PSOBBCharacterFile::create_from_dc_v2(const PSODC ret->inventory.decode_from_client(Version::DC_V2); uint8_t language = ret->inventory.language; ret->disp = dc.disp.to_bb(language, language); + ret->validation_flags = dc.validation_flags; ret->creation_timestamp = dc.creation_timestamp; ret->play_time_seconds = dc.play_time_seconds; ret->option_flags = dc.option_flags; @@ -586,6 +587,7 @@ shared_ptr PSOBBCharacterFile::create_from_gc_nte(const PSOG // not do this, so the data2 fields are already in the correct order here. uint8_t language = ret->inventory.language; ret->disp = gc_nte.disp.to_bb(language, language); + ret->validation_flags = gc_nte.validation_flags.load(); ret->creation_timestamp = gc_nte.creation_timestamp.load(); ret->play_time_seconds = gc_nte.play_time_seconds.load(); ret->option_flags = gc_nte.option_flags.load(); @@ -621,6 +623,7 @@ shared_ptr PSOBBCharacterFile::create_from_gc(const PSOGCCha // not do this, so the data2 fields are already in the correct order here. uint8_t language = ret->inventory.language; ret->disp = gc.disp.to_bb(language, language); + ret->validation_flags = gc.validation_flags.load(); ret->creation_timestamp = gc.creation_timestamp.load(); ret->play_time_seconds = gc.play_time_seconds.load(); ret->option_flags = gc.option_flags.load(); @@ -662,6 +665,7 @@ shared_ptr PSOBBCharacterFile::create_from_ep3(const PSOGCEp ret->inventory = ep3.inventory; uint8_t language = ret->inventory.language; ret->disp = ep3.disp.to_bb(language, language); + ret->validation_flags = ep3.validation_flags.load(); ret->creation_timestamp = ep3.creation_timestamp.load(); ret->play_time_seconds = ep3.play_time_seconds.load(); ret->option_flags = ep3.option_flags.load(); @@ -705,6 +709,7 @@ shared_ptr PSOBBCharacterFile::create_from_xb(const PSOXBCha ret->inventory.decode_from_client(Version::XB_V3); uint8_t language = ret->inventory.language; ret->disp = xb.disp.to_bb(language, language); + ret->validation_flags = xb.validation_flags; ret->creation_timestamp = xb.creation_timestamp.load(); ret->play_time_seconds = xb.play_time_seconds.load(); ret->option_flags = xb.option_flags.load(); @@ -797,6 +802,7 @@ PSODCV2CharacterFile PSOBBCharacterFile::to_dc_v2() const { ret.inventory.encode_for_client(Version::DC_V2, nullptr); ret.disp = this->disp.to_dcpcv3(language, language); ret.disp.visual.enforce_lobby_join_limits_for_version(Version::DC_V2); + ret.validation_flags = this->validation_flags.load(); ret.creation_timestamp = this->creation_timestamp.load(); ret.play_time_seconds = this->play_time_seconds.load(); ret.option_flags = this->option_flags.load(); @@ -838,6 +844,7 @@ PSOGCNTECharacterFileCharacter PSOBBCharacterFile::to_gc_nte() const { // not do this, so the data2 fields are already in the correct order here. ret.disp = this->disp.to_dcpcv3(language, language); ret.disp.visual.enforce_lobby_join_limits_for_version(Version::GC_V3); + ret.validation_flags = this->validation_flags.load(); ret.creation_timestamp = this->creation_timestamp.load(); ret.play_time_seconds = this->play_time_seconds.load(); ret.option_flags = this->option_flags.load(); @@ -875,6 +882,7 @@ PSOGCCharacterFile::Character PSOBBCharacterFile::to_gc() const { // not do this, so the data2 fields are already in the correct order here. ret.disp = this->disp.to_dcpcv3(language, language); ret.disp.visual.enforce_lobby_join_limits_for_version(Version::GC_V3); + ret.validation_flags = this->validation_flags.load(); ret.creation_timestamp = this->creation_timestamp.load(); ret.play_time_seconds = this->play_time_seconds.load(); ret.option_flags = this->option_flags.load(); @@ -919,6 +927,7 @@ PSOXBCharacterFileCharacter PSOBBCharacterFile::to_xb(uint64_t xb_user_id) const ret.inventory.encode_for_client(Version::XB_V3, nullptr); ret.disp = this->disp.to_dcpcv3(language, language); ret.disp.visual.enforce_lobby_join_limits_for_version(Version::XB_V3); + ret.validation_flags = this->validation_flags.load(); ret.creation_timestamp = this->creation_timestamp.load(); ret.play_time_seconds = this->play_time_seconds.load(); ret.option_flags = this->option_flags.load(); diff --git a/src/SaveFileFormats.hh b/src/SaveFileFormats.hh index 2620f98a..2ea7f019 100644 --- a/src/SaveFileFormats.hh +++ b/src/SaveFileFormats.hh @@ -295,7 +295,7 @@ struct PSODC112000CharacterFile { // See PSOGCCharacterFile::Character for descriptions of fields' meanings. /* 0000:---- */ PlayerInventory inventory; /* 034C:---- */ PlayerDispDataDCPCV3 disp; - /* 041C:0000 */ le_uint32_t flags = 0; + /* 041C:0000 */ le_uint32_t validation_flags = 0; /* 0420:0004 */ le_uint32_t creation_timestamp = 0; /* 0424:0008 */ le_uint32_t signature = 0xA205B064; /* 0428:000C */ le_uint32_t play_time_seconds = 0; @@ -311,7 +311,7 @@ struct PSODCV1CharacterFile { // See PSOGCCharacterFile::Character for descriptions of fields' meanings. /* 0000:---- */ PlayerInventory inventory; /* 034C:---- */ PlayerDispDataDCPCV3 disp; - /* 041C:0000 */ le_uint32_t flags = 0; + /* 041C:0000 */ le_uint32_t validation_flags = 0; /* 0420:0004 */ le_uint32_t creation_timestamp = 0; /* 0424:0008 */ le_uint32_t signature = 0xA205B064; /* 0428:000C */ le_uint32_t play_time_seconds = 0; @@ -334,7 +334,7 @@ struct PSODCV2CharacterFile { // See PSOGCCharacterFile::Character for descriptions of fields' meanings. /* 0000:---- */ PlayerInventory inventory; /* 034C:---- */ PlayerDispDataDCPCV3 disp; - /* 041C:0000 */ le_uint32_t flags = 0; + /* 041C:0000 */ le_uint32_t validation_flags = 0; /* 0420:0004 */ le_uint32_t creation_timestamp = 0; /* 0424:0008 */ le_uint32_t signature = 0xA205B064; /* 0428:000C */ le_uint32_t play_time_seconds = 0; @@ -379,7 +379,7 @@ struct PSOPCCharacterFile { // PSO______SYS and PSO______SYD struct Character { /* 0000 */ PlayerInventory inventory; /* 034C */ PlayerDispDataDCPCV3 disp; - /* 041C */ be_uint32_t flags = 0; + /* 041C */ be_uint32_t validation_flags = 0; /* 0420 */ be_uint32_t creation_timestamp = 0; /* 0424 */ be_uint32_t signature = 0x6C5D889E; /* 0428 */ be_uint32_t play_time_seconds = 0; @@ -416,7 +416,7 @@ struct PSOPCCharacterFile { // PSO______SYS and PSO______SYD struct PSOGCNTECharacterFileCharacter { /* 0000:---- */ PlayerInventoryBE inventory; /* 034C:---- */ PlayerDispDataDCPCV3BE disp; - /* 041C:0000 */ be_uint32_t flags = 0; + /* 041C:0000 */ be_uint32_t validation_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; @@ -448,19 +448,28 @@ struct PSOGCCharacterFile { // to the start of the second internal structure (second column). /* 0000:---- */ PlayerInventoryBE inventory; /* 034C:---- */ PlayerDispDataDCPCV3BE disp; - // Known bits in the flags field: + // Known bits in the validation_flags field: // 00000001: Character was not saved after disconnecting (and the message // about items being deleted is shown in the select menu) - // 00000002: Used, but purpose is unknown - // 00000008: Used, but purpose is unknown + // 00000002: Character has level out of range (< 0 or > max) + // 00000004: Character has EXP out of range for their current level + // 00000008: Character has one or more stats out of range (< 0 or > max) // 00000010: Character has ever possessed a hacked item, according to the // check_for_hacked_item function in DCv2 (TODO: Does this exist in V3+ // also? If so, is the logic the same?) - // 00000040: Used, but purpose is unknown - /* 041C:0000 */ be_uint32_t flags = 0; + // 00000020: Character has meseta out of range (< 0 or > 999999) + // 00000040: Character was loaded on a client that has "important" files + // modified (on GC, these files are ending_normal.sfd, psogc_j.sfd, + // psogc_j2.sfd, ult01.sfd, ult02.sfd, ult03.sfd, ult04.sfd, + // ItemPMT.prs, itemrt.gsl, itempt.gsl, and PlyLevelTbl.cpt). For files + // larger than 1000000 bytes (decimal), the game only checks the file's + // size and skips checksumming its contents. + /* 041C:0000 */ be_uint32_t validation_flags = 0; /* 0420:0004 */ be_uint32_t creation_timestamp = 0; // The signature field holds the value 0xA205B064, which is 2718281828 in - // decimal - approximately e * 10^9. It's unknown why Sega chose this value. + // decimal - approximately e * 10^9. It's unknown why Sega chose this + // value. On some other versions, this field has a different value; see the + // defaults in the other versions' structures. /* 0424:0008 */ be_uint32_t signature = 0xA205B064; /* 0428:000C */ be_uint32_t play_time_seconds = 0; // This field is a collection of several flags and small values. The known @@ -522,7 +531,7 @@ struct PSOGCEp3NTECharacter { // to the start of the second internal structure (second column). /* 0000:---- */ PlayerInventoryBE inventory; /* 034C:---- */ PlayerDispDataDCPCV3BE disp; - /* 041C:0000 */ be_uint32_t flags = 0; + /* 041C:0000 */ be_uint32_t validation_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; @@ -558,7 +567,7 @@ struct PSOGCEp3CharacterFile { // to the start of the second internal structure (second column). /* 0000:---- */ PlayerInventoryBE inventory; /* 034C:---- */ PlayerDispDataDCPCV3BE disp; - /* 041C:0000 */ be_uint32_t flags = 0; + /* 041C:0000 */ be_uint32_t validation_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; @@ -627,7 +636,7 @@ struct PSOXBCharacterFileCharacter { // Most fields have the same meanings as in PSOGCCharacterFile::Character. /* 0000:---- */ PlayerInventory inventory; /* 034C:---- */ PlayerDispDataDCPCV3 disp; - /* 041C:0000 */ le_uint32_t flags = 0; + /* 041C:0000 */ le_uint32_t validation_flags = 0; /* 0420:0004 */ le_uint32_t creation_timestamp = 0; /* 0424:0008 */ le_uint32_t signature = 0xC87ED5B1; /* 0428:000C */ le_uint32_t play_time_seconds = 0; @@ -670,7 +679,7 @@ struct PSOBBCharacterFile { /* 0000 */ PlayerInventory inventory; /* 034C */ PlayerDispDataBB disp; - /* 04DC */ le_uint32_t flags = 0; + /* 04DC */ le_uint32_t validation_flags = 0; /* 04E0 */ le_uint32_t creation_timestamp = 0; /* 04E4 */ le_uint32_t signature = 0xC87ED5B1; /* 04E8 */ le_uint32_t play_time_seconds = 0;