document what 96 command's value actually represents

This commit is contained in:
Martin Michelsen
2023-04-01 22:41:43 -07:00
parent 38469119ad
commit b6f71fffbf
5 changed files with 61 additions and 19 deletions
+7 -3
View File
@@ -1531,9 +1531,13 @@ struct C_Login_BB_93 {
// 96 (C->S): Character save information
struct C_CharSaveInfo_V3_BB_96 {
// This field appears to be a checksum or random stamp of some sort; it seems
// to be unique and constant per character.
le_uint32_t unknown_a1 = 0;
// The creation timestamp is the number of seconds since 12:00AM on 1 January
// 2000. Instead of computing this directly from the TBR (on PSO GC), the game
// uses localtime(), then converts that to the desired timestamp. The leap
// year correction in the latter phase of this computation seems incorrect; it
// adds a day in 2002, 2006, etc. instead of 2004, 2008, etc. See
// compute_psogc_timestamp in SaveFileFormats.cc for details.
le_uint32_t creation_timestamp = 0;
// This field counts certain events on a per-character basis. One of the
// relevant events is the act of sending a 96 command; another is the act of
// receiving a 97 command (to which the client responds with a B1 command).
+1 -1
View File
@@ -620,7 +620,7 @@ int main(int argc, char** argv) {
const auto& header = r.get<PSOGCIFileHeader>();
header.check();
const auto& system = r.get<PSOGCSystemFile>();
round1_seed = system.creation_internet_time;
round1_seed = system.creation_timestamp;
} else if (!seed.empty()) {
round1_seed = stoul(seed, nullptr, 16);
} else {
+3 -4
View File
@@ -15,10 +15,9 @@ class RareItemSet {
public:
struct Table {
// 0x280 in size; describes one difficulty, section ID, and episode
// TODO: It looks like this structure can actually vary. We see the offsets
// 0194 and 01B2 in the unused section, along with the value 1E (number of
// box rares). In PSOGC, these all appear to be the same size/format, but
// that's probably not strictly required to be the case.
// TODO: It looks like this structure can actually vary. In PSOGC, these all
// appear to be the same size/format, but that's probably not strictly
// required to be the case.
struct Drop {
uint8_t probability;
uint8_t item_code[3];
+20
View File
@@ -87,3 +87,23 @@ bool PSOGCIFileHeader::is_ep12() const {
bool PSOGCIFileHeader::is_ep3() const {
return (this->game_id[2] == 'S');
}
uint32_t compute_psogc_timestamp(
uint16_t year,
uint8_t month,
uint8_t day,
uint8_t hour,
uint8_t minute,
uint8_t second) {
static uint16_t month_start_day[12] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
uint32_t year_start_day = ((year - 1998) >> 2) + (year - 2000) * 365;
if ((((year - 1998) & 3) == 0) && (month < 3)) {
year_start_day--;
}
uint32_t res_day = (day - 1) + year_start_day + month_start_day[month - 1];
return second + (minute + (hour + (res_day * 24)) * 60) * 60;
}
+30 -11
View File
@@ -71,7 +71,10 @@ struct PSOGCSystemFile {
/* 000E */ be_uint16_t surround_sound_enabled;
/* 0010 */ parray<uint8_t, 0x100> unknown_a6;
/* 0110 */ parray<uint8_t, 8> unknown_a7;
/* 0118 */ be_uint32_t creation_internet_time; // Character file round1 seed
// This timestamp is the number of seconds since 12:00AM on 1 January 2000.
// This field is also used as the round1 seed for encrypting the character and
// Guild Card files.
/* 0118 */ be_uint32_t creation_timestamp;
/* 011C */
} __attribute__((packed));
@@ -116,9 +119,9 @@ struct PSOGCCharacterFile {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 041C */ be_uint32_t unknown_a1;
/* 0420 */ be_uint32_t save_token; // Sent in 96 command
/* 0420 */ be_uint32_t creation_timestamp;
/* 0424 */ parray<be_uint32_t, 3> unknown_a2;
/* 0430 */ be_uint32_t save_count; // Sent in 96 command
/* 0430 */ be_uint32_t save_count;
/* 0434 */ parray<uint8_t, 0x230> unknown_a3;
/* 0664 */ PlayerBank bank;
/* 192C */ GuildCardV3 guild_card;
@@ -148,19 +151,25 @@ struct PSOGCEp3CharacterFile {
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 041C */ be_uint32_t unknown_a1;
/* 0420 */ be_uint32_t save_token; // Sent in 96 command
/* 0424 */ parray<be_uint32_t, 3> unknown_a2;
// The signature field holds the value 0xA205B064, which is 2718281828 in
// decimal - approximately e * 10^9. It's unknown why Sega chose this value.
/* 0424 */ be_uint32_t signature;
/* 0428 */ be_uint32_t unknown_a2;
/* 042C */ be_uint32_t unknown_a3;
/* 0430 */ be_uint32_t save_count; // Sent in 96 command
/* 0434 */ parray<uint8_t, 0x498> unknown_a3;
/* 0434 */ parray<uint8_t, 0x1C> unknown_a4;
/* 0450 */ parray<uint8_t, 0x10> unknown_a5;
/* 0460 */ parray<uint8_t, 0x46C> unknown_a6;
/* 08CC */ GuildCardV3 guild_card;
/* 095C */ parray<PSOGCSaveFileSymbolChatEntry, 12> symbol_chats;
/* 0D7C */ parray<PSOGCSaveFileChatShortcutEntry, 20> chat_shortcuts;
/* 140C */ parray<uint8_t, 0xAC> unknown_a4;
/* 140C */ parray<uint8_t, 0xAC> unknown_a7;
/* 14B8 */ ptext<char, 0xAC> info_board;
/* 1564 */ parray<uint8_t, 0xF4> unknown_a5;
/* 1564 */ parray<uint8_t, 0xF4> unknown_a8;
/* 1658 */ Episode3::PlayerConfig ep3_config;
/* 39A8 */ be_uint32_t unknown_a7;
/* 39AC */ be_uint32_t unknown_a8;
/* 39B0 */ be_uint32_t unknown_a9;
/* 39A8 */ be_uint32_t unknown_a9;
/* 39AC */ be_uint32_t unknown_a10;
/* 39B0 */ be_uint32_t unknown_a11;
/* 39B4 */
} __attribute__((packed));
/* 00004 */ parray<Character, 7> characters;
@@ -201,7 +210,7 @@ struct PSOGCGuildCardFile {
} __attribute__((packed));
/* 00C4 */ parray<GuildCardEntry, 0xD2> entries;
/* D2C4 */ parray<GuildCardBE, 0x1C> blocked_senders;
/* E284 */ be_uint32_t unknown_a3;
/* E284 */ be_uint32_t creation_timestamp;
/* E288 */ be_uint32_t round2_seed;
/* E28C */
} __attribute__((packed));
@@ -285,3 +294,13 @@ std::string encrypt_gci_fixed_size_file_data_section(
return encrypt_gci_or_vms_v2_data_section<true>(
&encrypted, sizeof(StructT), round1_seed);
}
uint32_t compute_psogc_timestamp(
uint16_t year,
uint8_t month,
uint8_t day,
uint8_t hour,
uint8_t minute,
uint8_t second);