document what 96 command's value actually represents
This commit is contained in:
@@ -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
@@ -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
@@ -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];
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user