more save file format refinement
This commit is contained in:
+24
-21
@@ -582,23 +582,26 @@ struct DeckDefinition {
|
||||
} __attribute__((packed)); // 0x84 bytes in total
|
||||
|
||||
struct PlayerConfig {
|
||||
// The first offsets in the comments in this struct are relative to the start
|
||||
// of the 61/98 command; the second are relative to the start of the
|
||||
// Ep3PlayerDataSegment structure in the reverse-engineering project; the
|
||||
// third are relative to the start of this struct.
|
||||
// TODO: Fill in the unknown fields here by looking around callsites of
|
||||
// get_player_data_segment
|
||||
/* 0728:----:0000 */ parray<uint8_t, 0x154> unknown_a1;
|
||||
/* 087C:0000:0154 */ uint8_t is_encrypted;
|
||||
/* 087D:0001:0155 */ uint8_t basis;
|
||||
/* 087E:0002:0156 */ parray<uint8_t, 2> unused;
|
||||
/* 0000 */ ptext<char, 12> rank_text; // From B7 command
|
||||
/* 000C */ parray<uint8_t, 0x1C> unknown_a1;
|
||||
/* 0028 */ parray<be_uint16_t, 20> tech_menu_shortcut_entries;
|
||||
/* 0050 */ parray<be_uint32_t, 10> choice_search_config;
|
||||
/* 0078 */ parray<be_uint32_t, 0x30> scenario_progress;
|
||||
/* 0138 */ be_uint16_t unknown_a2;
|
||||
/* 013A */ be_uint16_t unknown_a3;
|
||||
/* 013C */ parray<uint8_t, 0x18> unknown_a4;
|
||||
/* 0154 */ uint8_t is_encrypted;
|
||||
/* 0155 */ uint8_t basis;
|
||||
/* 0156 */ parray<uint8_t, 2> unused;
|
||||
// The following fields (here through the beginning of decks) are encrypted
|
||||
// using the trivial algorithm, with the basis specified above, if
|
||||
// is_encrypted is equal to 1.
|
||||
// It appears the card counts field in this structure is actually 1000 (0x3E8)
|
||||
// bytes long, even though in every other place the counts array appears it's
|
||||
// 0x2F0 bytes long. They presumably did this because of the checksum logic.
|
||||
/* 0880:0004:0158 */ parray<uint8_t, 1000> card_counts;
|
||||
/* 0158 */ parray<uint8_t, 1000> card_counts;
|
||||
// These appear to be an attempt at checksumming the card counts array, but
|
||||
// the algorithm don't cover the entire array and instead reads from later
|
||||
// parts of this structure. This appears to be due to a copy/paste error in
|
||||
@@ -607,26 +610,26 @@ struct PlayerConfig {
|
||||
// [69] and puts the result in card_count_checksums[1], etc. Presumably they
|
||||
// intended to use 20 as the stride instead of 50, which would have exactly
|
||||
// covered the entire card_counts array.
|
||||
/* 0C68:03EC:0540 */ parray<be_uint16_t, 50> card_count_checksums;
|
||||
/* 0540 */ parray<be_uint16_t, 50> card_count_checksums;
|
||||
// Yes, these are actually 64-bit integers. They include card IDs and some
|
||||
// other data, encoded in a way I don't fully understand yet.
|
||||
/* 0CCC:0450:05A4 */ parray<be_uint64_t, 0x1C2> unknown_a4;
|
||||
/* 1ADC:1260:13B4 */ parray<uint8_t, 0x80> unknown_a7;
|
||||
/* 1B5C:12E0:1434 */ parray<DeckDefinition, 25> decks;
|
||||
/* 2840:1FC4:2118 */ parray<uint8_t, 0x08> unknown_a8;
|
||||
/* 2848:1FCC:2120 */ be_uint32_t offline_clv_exp; // CLvOff = this / 100
|
||||
/* 284C:1FD0:2124 */ be_uint32_t online_clv_exp; // CLvOn = this / 100
|
||||
/* 05A4 */ parray<be_uint64_t, 0x1C2> unknown_a5;
|
||||
/* 13B4 */ parray<uint8_t, 0x80> unknown_a7;
|
||||
/* 1434 */ parray<DeckDefinition, 25> decks;
|
||||
/* 2118 */ parray<uint8_t, 0x08> unknown_a8;
|
||||
/* 2120 */ be_uint32_t offline_clv_exp; // CLvOff = this / 100
|
||||
/* 2124 */ be_uint32_t online_clv_exp; // CLvOn = this / 100
|
||||
struct PlayerReference {
|
||||
/* 00 */ be_uint32_t guild_card_number;
|
||||
/* 04 */ ptext<char, 0x18> player_name;
|
||||
} __attribute__((packed));
|
||||
// TODO: What do these player references mean? When are entries added to or
|
||||
// removed from this list?
|
||||
/* 2850:1FD4:2128 */ parray<PlayerReference, 9> unknown_a9;
|
||||
/* 294C:20D0:2224 */ parray<uint8_t, 0x50> unknown_a10;
|
||||
/* 299C:2120:2274 */ ptext<char, 0x10> name;
|
||||
/* 29AC:2130:2284 */ parray<uint8_t, 0xCC> unknown_a11;
|
||||
/* 2A78:21FC:2350 */
|
||||
/* 2128 */ parray<PlayerReference, 9> unknown_a9;
|
||||
/* 2224 */ parray<uint8_t, 0x50> unknown_a10;
|
||||
/* 2274 */ ptext<char, 0x10> name;
|
||||
/* 2284 */ parray<uint8_t, 0xCC> unknown_a11;
|
||||
/* 2350 */
|
||||
|
||||
void decrypt();
|
||||
void encrypt(uint8_t basis);
|
||||
|
||||
+60
-25
@@ -135,8 +135,26 @@ struct PSOGCCharacterFile {
|
||||
// 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;
|
||||
/* 0428 */ be_uint32_t play_time_seconds;
|
||||
// This field is a collection of several flags and small values. The known
|
||||
// fields are:
|
||||
// ------zA BCDEFG-- HHHIIIJJ KLMNOPQR
|
||||
// z = Function key setting (BB; 0 = menu shortcuts; 1 = chat shortcuts).
|
||||
// This bit is unused by PSO GC.
|
||||
// A = Keyboard controls (BB; 0 = on; 1 = off). Note that A is also used
|
||||
// by PSO GC, but its function is currently unknown.
|
||||
// G = Choice search setting (0 = enabled; 1 = disabled)
|
||||
// H = Player lobby labels (0 = name; 1 = name, language, and level;
|
||||
// 2 = W/D counts; 3 = challenge rank; 4 = nothing)
|
||||
// I = Idle disconnect time (0 = 15 mins; 1 = 30 mins; 2 = 45 mins;
|
||||
// 3 = 60 mins; 4 or 5: immediately; 6: 16 seconds; 7: 32 seconds).
|
||||
// Obviously the behaviors for 4-7 are unintended; this is the result
|
||||
// of a missing bounds check.
|
||||
// J = Message speed (0 = slow; 1 = normal; 2 = fast; 3 = very fast)
|
||||
// P = Cursor position (0 = saved; 1 = non-saved)
|
||||
// Q = Button config (0 = normal; 1 = L/R reversed)
|
||||
// R = Map direction (0 = non-fixed; 1 = fixed)
|
||||
/* 042C */ be_uint32_t option_flags;
|
||||
/* 0430 */ be_uint32_t save_count;
|
||||
/* 0434 */ parray<uint8_t, 0x230> unknown_a4;
|
||||
/* 0664 */ PlayerBank bank;
|
||||
@@ -163,37 +181,54 @@ struct PSOGCCharacterFile {
|
||||
struct PSOGCEp3CharacterFile {
|
||||
/* 00000 */ be_uint32_t checksum; // crc32 of this field (as 0) through end of struct
|
||||
struct Character {
|
||||
/* 0000 */ PlayerInventory inventory;
|
||||
/* 034C */ PlayerDispDataDCPCV3 disp;
|
||||
/* 041C */ be_uint32_t unknown_a1;
|
||||
/* 0420 */ be_uint32_t creation_timestamp;
|
||||
/* 0424 */ be_uint32_t signature; // Same value as for Episodes 1&2 (above)
|
||||
/* 0428 */ be_uint32_t unknown_a2;
|
||||
/* 042C */ be_uint32_t unknown_a3;
|
||||
/* 0430 */ be_uint32_t save_count;
|
||||
/* 0434 */ parray<uint8_t, 0x1C> unknown_a4;
|
||||
/* 0450 */ parray<uint8_t, 0x10> unknown_a5;
|
||||
// This structure is internally split into two by the game. The offsets here
|
||||
// are relative to the start of this structure (first column), and relative
|
||||
// to the start of the second internal structure (second column).
|
||||
/* 0000:---- */ PlayerInventory inventory;
|
||||
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
|
||||
/* 041C:0000 */ be_uint32_t unknown_a1;
|
||||
/* 0420:0004 */ be_uint32_t creation_timestamp;
|
||||
/* 0424:0008 */ be_uint32_t signature; // Same value as for Episodes 1&2 (above)
|
||||
/* 0428:000C */ be_uint32_t play_time_seconds;
|
||||
// See the comment in PSOGCCharacterFile::Character about what the bits in
|
||||
// this field mean.
|
||||
/* 042C:0010 */ be_uint32_t option_flags;
|
||||
/* 0430:0014 */ be_uint32_t save_count;
|
||||
/* 0434:0018 */ parray<uint8_t, 0x1C> unknown_a2;
|
||||
/* 0450:0034 */ parray<uint8_t, 0x10> unknown_a3;
|
||||
// seq_vars is an array of 1024 bits, which contain all the Episode 3 quest
|
||||
// progress flags. This includes things like which maps are unlocked, which
|
||||
// NPC decks are unlocked, and whether the player has a VIP card or not.
|
||||
/* 0460 */ parray<uint8_t, 0x400> seq_vars;
|
||||
/* 0860 */ parray<uint8_t, 0x6C> unknown_a6;
|
||||
/* 08CC */ GuildCardV3 guild_card;
|
||||
/* 095C */ parray<PSOGCSaveFileSymbolChatEntry, 12> symbol_chats;
|
||||
/* 0D7C */ parray<PSOGCSaveFileChatShortcutEntry, 20> chat_shortcuts;
|
||||
/* 140C */ parray<uint8_t, 0xAC> unknown_a7;
|
||||
/* 14B8 */ ptext<char, 0xAC> info_board;
|
||||
/* 1564 */ parray<uint8_t, 0xF4> unknown_a8;
|
||||
/* 1658 */ Episode3::PlayerConfig ep3_config;
|
||||
/* 39A8 */ be_uint32_t unknown_a9;
|
||||
/* 39AC */ be_uint32_t unknown_a10;
|
||||
/* 39B0 */ be_uint32_t unknown_a11;
|
||||
/* 39B4 */
|
||||
/* 0460:0044 */ parray<uint8_t, 0x400> seq_vars;
|
||||
/* 0860:0444 */ be_uint32_t unknown_a4;
|
||||
/* 0864:0448 */ be_uint32_t unknown_a5;
|
||||
/* 0868:044C */ be_uint32_t unknown_a6;
|
||||
/* 086C:0450 */ parray<uint8_t, 0x60> unknown_a7;
|
||||
/* 08CC:04B0 */ GuildCardV3 guild_card;
|
||||
/* 095C:0540 */ parray<PSOGCSaveFileSymbolChatEntry, 12> symbol_chats;
|
||||
/* 0D7C:0960 */ parray<PSOGCSaveFileChatShortcutEntry, 20> chat_shortcuts;
|
||||
/* 140C:0FF0 */ ptext<char, 0xAC> auto_reply;
|
||||
/* 14B8:109C */ ptext<char, 0xAC> info_board;
|
||||
/* 1564:1148 */ be_uint16_t win_count;
|
||||
/* 1566:114A */ be_uint16_t lose_count;
|
||||
/* 1568:114C */ parray<be_uint16_t, 5> unknown_a8;
|
||||
/* 1572:1156 */ parray<uint8_t, 2> unused;
|
||||
/* 1574:1158 */ parray<be_uint32_t, 2> unknown_a9;
|
||||
/* 157C:1160 */ parray<uint8_t, 0xDC> unknown_a10;
|
||||
/* 1658:123C */ Episode3::PlayerConfig ep3_config;
|
||||
/* 39A8:358C */ be_uint32_t unknown_a11;
|
||||
/* 39AC:3590 */ be_uint32_t unknown_a12;
|
||||
/* 39B0:3594 */ be_uint32_t unknown_a13;
|
||||
/* 39B4:3598 */
|
||||
} __attribute__((packed));
|
||||
/* 00004 */ parray<Character, 7> characters;
|
||||
/* 193F0 */ ptext<char, 0x10> serial_number; // As %08X (not decimal)
|
||||
/* 19400 */ ptext<char, 0x10> access_key;
|
||||
/* 19410 */ ptext<char, 0x10> password;
|
||||
// In Episode 3, this field still exists, but is unused since BGM test was
|
||||
// removed from the options menu in favor of the jukebox. The jukebox is
|
||||
// accessible online only, and which songs are available there is controlled
|
||||
// by the B7 command sent by the server instead.
|
||||
/* 19420 */ be_uint64_t bgm_test_songs_unlocked;
|
||||
/* 19428 */ be_uint32_t save_count;
|
||||
// This is an array of 1000 bits, represented here as 128 bytes, the last few
|
||||
|
||||
Reference in New Issue
Block a user