add DC v2 save file format

This commit is contained in:
Martin Michelsen
2024-05-11 18:16:53 -07:00
parent a0126bd6b5
commit dc7c3eb58c
10 changed files with 146 additions and 67 deletions
+68 -33
View File
@@ -132,11 +132,11 @@ struct SaveFileSymbolChatEntryT {
} __packed__;
using SaveFileSymbolChatEntryPC = SaveFileSymbolChatEntryT<false, TextEncoding::UTF16, 0x18>;
using SaveFileSymbolChatEntryGC = SaveFileSymbolChatEntryT<true, TextEncoding::MARKED, 0x18>;
using SaveFileSymbolChatEntryXB = SaveFileSymbolChatEntryT<false, TextEncoding::MARKED, 0x18>;
using SaveFileSymbolChatEntryDCXB = SaveFileSymbolChatEntryT<false, TextEncoding::MARKED, 0x18>;
using SaveFileSymbolChatEntryBB = SaveFileSymbolChatEntryT<false, TextEncoding::UTF16, 0x14>;
check_struct_size(SaveFileSymbolChatEntryPC, 0x70);
check_struct_size(SaveFileSymbolChatEntryGC, 0x58);
check_struct_size(SaveFileSymbolChatEntryXB, 0x58);
check_struct_size(SaveFileSymbolChatEntryDCXB, 0x58);
check_struct_size(SaveFileSymbolChatEntryBB, 0x68);
template <bool IsBigEndian>
@@ -167,12 +167,12 @@ using WordSelectMessageBE = WordSelectMessageT<true>;
check_struct_size(WordSelectMessage, 0x1C);
check_struct_size(WordSelectMessageBE, 0x1C);
template <bool IsBigEndian, TextEncoding Encoding>
template <bool IsBigEndian, TextEncoding Encoding, size_t MaxChars>
struct SaveFileChatShortcutEntryT {
using U32T = std::conditional_t<IsBigEndian, be_uint32_t, le_uint32_t>;
union Definition {
pstring<Encoding, 0x50> text;
pstring<Encoding, MaxChars> text;
WordSelectMessageT<IsBigEndian> word_select;
SymbolChatT<IsBigEndian> symbol_chat;
@@ -184,14 +184,14 @@ struct SaveFileChatShortcutEntryT {
}
} __packed__;
/* GC:BB */
/* 00:00 */ U32T type; // 1 = text, 2 = word select, 3 = symbol chat
/* 04:04 */ Definition definition;
/* 54:A4 */
/* DC:GC:BB */
/* 00:00:00 */ U32T type; // 1 = text, 2 = word select, 3 = symbol chat
/* 04:04:04 */ Definition definition;
/* 40:54:A4 */
template <bool RetIsBigEndian, TextEncoding RetEncoding>
SaveFileChatShortcutEntryT<RetIsBigEndian, RetEncoding> convert(uint8_t language) const {
SaveFileChatShortcutEntryT<RetIsBigEndian, RetEncoding> ret;
template <bool RetIsBigEndian, TextEncoding RetEncoding, size_t RetMaxSize>
SaveFileChatShortcutEntryT<RetIsBigEndian, RetEncoding, RetMaxSize> convert(uint8_t language) const {
SaveFileChatShortcutEntryT<RetIsBigEndian, RetEncoding, RetMaxSize> ret;
ret.type = this->type.load();
switch (ret.type) {
case 1:
@@ -209,13 +209,49 @@ struct SaveFileChatShortcutEntryT {
return ret;
}
} __packed__;
using SaveFileShortcutEntryGC = SaveFileChatShortcutEntryT<true, TextEncoding::MARKED>;
using SaveFileShortcutEntryXB = SaveFileChatShortcutEntryT<false, TextEncoding::MARKED>;
using SaveFileShortcutEntryBB = SaveFileChatShortcutEntryT<false, TextEncoding::UTF16>;
using SaveFileShortcutEntryDC = SaveFileChatShortcutEntryT<false, TextEncoding::MARKED, 0x3C>;
using SaveFileShortcutEntryGC = SaveFileChatShortcutEntryT<true, TextEncoding::MARKED, 0x50>;
using SaveFileShortcutEntryXB = SaveFileChatShortcutEntryT<false, TextEncoding::MARKED, 0x50>;
using SaveFileShortcutEntryBB = SaveFileChatShortcutEntryT<false, TextEncoding::UTF16, 0x50>;
check_struct_size(SaveFileShortcutEntryDC, 0x40);
check_struct_size(SaveFileShortcutEntryGC, 0x54);
check_struct_size(SaveFileShortcutEntryXB, 0x54);
check_struct_size(SaveFileShortcutEntryBB, 0xA4);
struct PSODCV2CharacterFile {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 041C:0000 */ le_uint32_t 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;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint32_t save_count = 0;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
/* 0460:0044 */ QuestFlags quest_flags;
/* 0660:0244 */ PlayerBank60 bank;
/* 0C08:07EC */ GuildCardDC guild_card;
/* 0C85:0869 */ parray<uint8_t, 3> unknown_s1; // Probably actually unused
/* 0C88:086C */ parray<SaveFileSymbolChatEntryDCXB, 12> symbol_chats;
/* 10A8:0C8C */ parray<SaveFileShortcutEntryDC, 20> shortcuts;
/* 15A8:118C */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
/* 15B8:119C */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
/* 15C8:11AC */ PlayerRecordsBattle battle_records;
/* 15E0:11C4 */ PlayerRecordsChallengeDC challenge_records;
/* 1680:1264 */ parray<le_uint16_t, 20> tech_menu_shortcut_entries;
// The Choice Search config is stored here as 32-bit integers, even though
// it's always represented with 16-bit integers in the C0 command. The order
// of the entries here is the same (that is, the first two of these ints are
// entries[0], the second two are entries[1], etc.).
/* 16A8:128C */ parray<le_uint32_t, 10> choice_search_config;
/* 16D0:12B4 */ parray<uint8_t, 4> unknown_a2;
/* 16D4:12B8 */ pstring<TextEncoding::ASCII, 0x10> v2_serial_number;
/* 16E4:12C8 */ pstring<TextEncoding::ASCII, 0x10> v2_access_key;
/* 16F4:12D8 */
} __packed_ws__(PSODCV2CharacterFile, 0x16F4);
struct PSOGCCharacterFile {
/* 00000 */ be_uint32_t checksum;
struct Character {
@@ -257,11 +293,11 @@ struct PSOGCCharacterFile {
// R = Map direction (0 = non-fixed; 1 = fixed)
/* 042C:0010 */ be_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ be_uint32_t save_count = 0;
/* 0434:0018 */ parray<uint8_t, 0x1C> unknown_a2;
/* 0450:0034 */ parray<uint8_t, 0x10> unknown_a3;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
/* 0460:0044 */ QuestFlags quest_flags;
/* 0660:0244 */ be_uint32_t death_count = 0;
/* 0664:0248 */ PlayerBankBE bank;
/* 0664:0248 */ PlayerBank200BE bank;
/* 192C:1510 */ GuildCardGCBE guild_card;
/* 19BC:15A0 */ parray<SaveFileSymbolChatEntryGC, 12> symbol_chats;
/* 1DDC:19C0 */ parray<SaveFileShortcutEntryGC, 20> shortcuts;
@@ -307,19 +343,17 @@ struct PSOGCEp3CharacterFile {
// 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;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
// seq_vars is an array of 8192 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:0044 */ parray<uint8_t, 0x400> seq_vars;
/* 0860:0444 */ be_uint32_t death_count;
// The following three fields appear to actually be a PlayerBank structure
// with only 4 item slots instead of 200. They presumably didn't completely
// remove the bank in Ep3 because they would have to change too much code.
/* 0864:0448 */ be_uint32_t num_bank_items;
/* 0868:044C */ be_uint32_t bank_meseta;
/* 086C:0450 */ parray<PlayerBankItemBE, 4> bank_items;
// Curiously, Episode 3 characters do have item banks, but there are only 4
// item slots. Sega presumably didn't completely remove the bank in Ep3
// because they would have to change too much code.
/* 0864:0448 */ PlayerBankT<4, true> bank;
/* 08CC:04B0 */ GuildCardGCBE guild_card;
/* 095C:0540 */ parray<SaveFileSymbolChatEntryGC, 12> symbol_chats;
/* 0D7C:0960 */ parray<SaveFileShortcutEntryGC, 20> chat_shortcuts;
@@ -410,13 +444,13 @@ struct PSOXBCharacterFileCharacter {
/* 0428:000C */ le_uint32_t play_time_seconds = 0;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint32_t save_count = 0;
/* 0434:0018 */ parray<uint8_t, 0x1C> unknown_a2;
/* 0450:0034 */ parray<uint8_t, 0x10> unknown_a3;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
/* 0460:0044 */ QuestFlags quest_flags;
/* 0660:0244 */ le_uint32_t death_count = 0;
/* 0664:0248 */ PlayerBank bank;
/* 0664:0248 */ PlayerBank200 bank;
/* 192C:1510 */ GuildCardXB guild_card;
/* 1B58:173C */ parray<SaveFileSymbolChatEntryXB, 12> symbol_chats;
/* 1B58:173C */ parray<SaveFileSymbolChatEntryDCXB, 12> symbol_chats;
/* 1F78:1B5C */ parray<SaveFileShortcutEntryXB, 16> shortcuts;
/* 24B8:209C */ pstring<TextEncoding::MARKED, 0xAC> auto_reply;
/* 2518:20FC */ pstring<TextEncoding::MARKED, 0xAC> info_board;
@@ -759,7 +793,7 @@ struct PSOBBCharacterFile {
/* 04F0 */ le_uint32_t save_count = 0;
/* 04F4 */ QuestFlags quest_flags;
/* 06F4 */ le_uint32_t death_count = 0;
/* 06F8 */ PlayerBank bank;
/* 06F8 */ PlayerBank200 bank;
/* 19C0 */ GuildCardBB guild_card;
/* 1AC8 */ le_uint32_t unknown_a3 = 0;
/* 1ACC */ parray<SaveFileSymbolChatEntryBB, 0x0C> symbol_chats;
@@ -795,8 +829,9 @@ struct PSOBBCharacterFile {
uint8_t language,
const PlayerDispDataBBPreview& preview,
std::shared_ptr<const LevelTable> level_table);
static std::shared_ptr<PSOBBCharacterFile> create_from_gc(const PSOGCCharacterFile::Character& gc_char);
static std::shared_ptr<PSOBBCharacterFile> create_from_xb(const PSOXBCharacterFileCharacter& xb_char);
static std::shared_ptr<PSOBBCharacterFile> create_from_dc_v2(const PSODCV2CharacterFile& dc);
static std::shared_ptr<PSOBBCharacterFile> create_from_gc(const PSOGCCharacterFile::Character& gc);
static std::shared_ptr<PSOBBCharacterFile> create_from_xb(const PSOXBCharacterFileCharacter& xb);
void add_item(const ItemData& item, const ItemData::StackLimits& limits);
ItemData remove_item(uint32_t item_id, uint32_t amount, const ItemData::StackLimits& limits);
@@ -856,7 +891,7 @@ struct LegacySavedPlayerDataBB { // .nsc file format
/* 0028 */ PlayerRecordsBattle battle_records;
/* 0040 */ PlayerDispDataBBPreview preview;
/* 00BC */ pstring<TextEncoding::UTF16, 0x00AC> auto_reply;
/* 0214 */ PlayerBank bank;
/* 0214 */ PlayerBank200 bank;
/* 14DC */ PlayerRecordsChallengeBB challenge_records;
/* 161C */ PlayerDispDataBB disp;
/* 17AC */ pstring<TextEncoding::UTF16, 0x0058> guild_card_description;