describe DC save file formats; add decrypt/encrypt actions

This commit is contained in:
Martin Michelsen
2024-11-03 21:24:48 -08:00
parent ac20d0c7d4
commit 0522b539c4
22 changed files with 544 additions and 214 deletions
+185 -124
View File
@@ -36,6 +36,12 @@ struct PSOVMSFileHeader {
/* 0080 */ // parray<parray<uint8_t, 0x200>, num_icons> icon;
bool checksum_correct() const;
void check() const;
inline size_t icon_data_size() const {
return this->num_icons * 0x200;
}
bool is_v2() const;
} __packed_ws__(PSOVMSFileHeader, 0x80);
struct PSOGCIFileHeader {
@@ -292,118 +298,139 @@ struct PSOBBBaseSystemFile : PSOBBMinimalSystemFile {
// Character files
struct PSODCNTECharacterFile {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 041C:0000 */ le_uint32_t validation_flags = 0;
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
/* 0424:0008 */ le_uint32_t signature = 0xBB40711D;
/* 0428:000C */ le_uint32_t play_time_seconds = 0;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint16_t save_count_since_last_inventory_erasure = 1;
/* 0432:0016 */ le_uint16_t inventory_erasure_count = 0;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
// TODO: Figure out how quest flags work; it's obviously different from 0x80
// bytes per difficulty like in v1. Is it just 2048 flags shared across all
// difficulties, instead of 1024 in each difficulty?
/* 0460:0044 */ parray<uint8_t, 0x100> quest_flags;
/* 0560:0144 */ le_uint16_t bank_meseta;
/* 0562:0146 */ le_uint16_t num_bank_items;
/* 0564:0148 */ parray<ItemData, 60> bank_items;
/* 0A14:05F8 */ GuildCardDCNTE guild_card;
/* 0A8F:0673 */ uint8_t unknown_s1; // Probably actually unused
/* 0A90:0674 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
/* 0AA0:0684 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
/* 0AB0:0694 */
} __packed_ws__(PSODCNTECharacterFile, 0xAB0);
/* 0000 */ le_uint32_t checksum = 0;
struct Character {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 041C:0000 */ le_uint32_t validation_flags = 0;
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
/* 0424:0008 */ le_uint32_t signature = 0xBB40711D;
/* 0428:000C */ le_uint32_t play_time_seconds = 0;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint16_t save_count_since_last_inventory_erasure = 1;
/* 0432:0016 */ le_uint16_t inventory_erasure_count = 0;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
// TODO: Figure out how quest flags work; it's obviously different from 0x80
// bytes per difficulty like in v1. Is it just 2048 flags shared across all
// difficulties, instead of 1024 in each difficulty?
/* 0460:0044 */ parray<uint8_t, 0x100> quest_flags;
/* 0560:0144 */ le_uint16_t bank_meseta;
/* 0562:0146 */ le_uint16_t num_bank_items;
/* 0564:0148 */ parray<ItemData, 60> bank_items;
/* 0A14:05F8 */ GuildCardDCNTE guild_card;
/* 0A8F:0673 */ uint8_t unknown_s1; // Probably actually unused
/* 0A90:0674 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
/* 0AA0:0684 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
/* 0AB0:0694 */
} __packed_ws__(Character, 0xAB0);
/* 0004 */ Character character;
/* 0AB4 */ le_uint32_t round2_seed = 0;
/* 0AB8 */
} __packed_ws__(PSODCNTECharacterFile, 0xAB8);
struct PSODC112000CharacterFile {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 041C:0000 */ le_uint32_t validation_flags = 0;
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
/* 0424:0008 */ le_uint32_t signature = 0xBB40711D;
/* 0428:000C */ le_uint32_t play_time_seconds = 0;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint16_t save_count_since_last_inventory_erasure = 1;
/* 0432:0016 */ le_uint16_t inventory_erasure_count = 0;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
// TODO: Figure out how quest flags work; it's obviously different from 0x80
// bytes per difficulty like in v1. Is it just 2048 flags shared across all
// difficulties, instead of 1024 in each difficulty?
/* 0460:0044 */ parray<uint8_t, 0x100> quest_flags;
/* 0560:0144 */ le_uint16_t bank_meseta;
/* 0562:0146 */ le_uint16_t num_bank_items;
/* 0564:0148 */ parray<ItemData, 60> bank_items;
/* 0A14:05F8 */ GuildCardDC guild_card;
/* 0A91:0675 */ parray<uint8_t, 3> unknown_s1; // Probably actually unused
/* 0A94:0678 */ parray<SaveFileSymbolChatEntryDCXB, 12> symbol_chats;
/* 0EB4:0A98 */ parray<SaveFileShortcutEntryDC, 20> shortcuts;
/* 13B4:0F98 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
/* 13C4:0FA8 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
/* 13D4:0FB8 */
} __packed_ws__(PSODC112000CharacterFile, 0x13D4);
/* 0000 */ le_uint32_t checksum = 0;
struct Character {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 041C:0000 */ le_uint32_t validation_flags = 0;
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
/* 0424:0008 */ le_uint32_t signature = 0xBB40711D;
/* 0428:000C */ le_uint32_t play_time_seconds = 0;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint16_t save_count_since_last_inventory_erasure = 1;
/* 0432:0016 */ le_uint16_t inventory_erasure_count = 0;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
// TODO: Figure out how quest flags work; it's obviously different from 0x80
// bytes per difficulty like in v1. Is it just 2048 flags shared across all
// difficulties, instead of 1024 in each difficulty?
/* 0460:0044 */ parray<uint8_t, 0x100> quest_flags;
/* 0560:0144 */ le_uint16_t bank_meseta;
/* 0562:0146 */ le_uint16_t num_bank_items;
/* 0564:0148 */ parray<ItemData, 60> bank_items;
/* 0A14:05F8 */ GuildCardDC guild_card;
/* 0A91:0675 */ parray<uint8_t, 3> unknown_s1; // Probably actually unused
/* 0A94:0678 */ parray<SaveFileSymbolChatEntryDCXB, 12> symbol_chats;
/* 0EB4:0A98 */ parray<SaveFileShortcutEntryDC, 20> shortcuts;
/* 13B4:0F98 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
/* 13C4:0FA8 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
/* 13D4:0FB8 */
} __packed_ws__(Character, 0x13D4);
/* 0004 */ Character character;
/* 13D8 */ le_uint32_t round2_seed = 0;
} __packed_ws__(PSODC112000CharacterFile, 0x13DC);
struct PSODCV1CharacterFile {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 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;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint32_t save_count = 1;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
/* 0460:0044 */ QuestFlagsV1 quest_flags;
/* 05E0:01C4 */ PlayerBank60 bank;
/* 0B88:076C */ GuildCardDC guild_card;
/* 0C05:07E9 */ parray<uint8_t, 3> unknown_s1; // Probably actually unused
/* 0C08:07EC */ parray<SaveFileSymbolChatEntryDCXB, 12> symbol_chats;
/* 1028:0C0C */ parray<SaveFileShortcutEntryDC, 20> shortcuts;
/* 1528:110C */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
/* 1538:111C */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
/* 1548:112C */
} __packed_ws__(PSODCV1CharacterFile, 0x1548);
/* 0000 */ le_uint32_t checksum = 0;
struct Character {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 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;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint32_t save_count = 1;
/* 0434:0018 */ pstring<TextEncoding::ASCII, 0x1C> ppp_username;
/* 0450:0034 */ pstring<TextEncoding::ASCII, 0x10> ppp_password;
/* 0460:0044 */ QuestFlagsV1 quest_flags;
/* 05E0:01C4 */ PlayerBank60 bank;
/* 0B88:076C */ GuildCardDC guild_card;
/* 0C05:07E9 */ parray<uint8_t, 3> unknown_s1; // Probably actually unused
/* 0C08:07EC */ parray<SaveFileSymbolChatEntryDCXB, 12> symbol_chats;
/* 1028:0C0C */ parray<SaveFileShortcutEntryDC, 20> shortcuts;
/* 1528:110C */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
/* 1538:111C */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
/* 1548:112C */
} __packed_ws__(Character, 0x1548);
/* 0004 */ Character character;
/* 154C */ le_uint32_t round2_seed = 0;
} __packed_ws__(PSODCV1CharacterFile, 0x1550);
struct PSODCV2CharacterFile {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 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;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint32_t save_count = 1;
/* 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 represented with 16-bit integers in the various commands that send it
// to and from the server. 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);
/* 0000 */ le_uint32_t checksum = 0;
struct Character {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 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;
/* 042C:0010 */ le_uint32_t option_flags = 0x00040058;
/* 0430:0014 */ le_uint32_t save_count = 1;
/* 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 represented with 16-bit integers in the various commands that send it
// to and from the server. 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__(Character, 0x16F4);
/* 0004 */ Character character;
/* 16F0 */ le_uint32_t round2_seed = 0;
} __packed_ws__(PSODCV2CharacterFile, 0x16FC);
struct PSOPCCharacterFile { // PSO______SYS and PSO______SYD
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
@@ -517,7 +544,7 @@ struct PSOGCCharacterFile {
// ------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
// A = Keyboard controls (BB; 0 = on; 1 = off). This field 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;
@@ -555,7 +582,7 @@ struct PSOGCCharacterFile {
/* 2794:2378 */ be_uint32_t unknown_f8 = 0;
/* 2798:237C */
} __packed_ws__(Character, 0x2798);
/* 00004 */ parray<Character, 7> characters;
/* 00004 */ parray<Character, 7> characters; // 0-3: main chars, 4-6: temps
/* 1152C */ pstring<TextEncoding::ASCII, 0x10> serial_number; // As %08X (not decimal)
/* 1153C */ pstring<TextEncoding::ASCII, 0x10> access_key;
/* 1154C */ pstring<TextEncoding::ASCII, 0x10> password;
@@ -760,13 +787,13 @@ struct PSOBBCharacterFile {
uint8_t language,
const PlayerDispDataBBPreview& preview,
std::shared_ptr<const LevelTable> level_table);
static std::shared_ptr<PSOBBCharacterFile> create_from_dc_v2(const PSODCV2CharacterFile& dc);
static std::shared_ptr<PSOBBCharacterFile> create_from_dc_v2(const PSODCV2CharacterFile::Character& dc);
static std::shared_ptr<PSOBBCharacterFile> create_from_gc_nte(const PSOGCNTECharacterFileCharacter& gc_nte);
static std::shared_ptr<PSOBBCharacterFile> create_from_gc(const PSOGCCharacterFile::Character& gc);
static std::shared_ptr<PSOBBCharacterFile> create_from_ep3(const PSOGCEp3CharacterFile::Character& ep3);
static std::shared_ptr<PSOBBCharacterFile> create_from_xb(const PSOXBCharacterFileCharacter& xb);
PSODCV2CharacterFile to_dc_v2() const;
PSODCV2CharacterFile::Character to_dc_v2() const;
PSOGCNTECharacterFileCharacter to_gc_nte() const;
PSOGCCharacterFile::Character to_gc() const;
PSOXBCharacterFileCharacter to_xb(uint64_t xb_user_id) const;
@@ -811,11 +838,45 @@ void save_psochar(
////////////////////////////////////////////////////////////////////////////////
// Guild Card files
struct PSODCV2GuildCardFile {
// TODO: Fill this out.
/* 0000 */ parray<uint8_t, 0x330C> unknown_a1;
/* 330C */
} __packed_ws__(PSODCV2GuildCardFile, 0x330C);
struct PSODCNTEGuildCardFile {
// Note: DC NTE does not encrypt the Guild Card file
struct Entry {
/* 0000 */ GuildCardDCNTE guild_card;
/* 007B */ uint8_t unknown_a1 = 0; // Probably actually unused
/* 007C */
} __packed_ws__(Entry, 0x7C);
/* 0000 */ parray<Entry, 100> entries;
/* 3070 */
} __packed_ws__(PSODCNTEGuildCardFile, 0x3070);
struct PSODCGuildCardFileEntry {
/* 0000 */ GuildCardDC guild_card;
/* 007D */ parray<uint8_t, 3> unknown_a1 = 0; // Probably actually unused
/* 0080 */
} __packed_ws__(PSODCGuildCardFileEntry, 0x80);
struct PSODC112000GuildCardFile {
// Note: 11/2000 does not encrypt the Guild Card file
/* 0000 */ parray<PSODCGuildCardFileEntry, 100> entries;
/* 3200 */
} __packed_ws__(PSODC112000GuildCardFile, 0x3200);
struct PSODCV1V2GuildCardFile {
struct EncryptedSection {
/* 0000 */ le_uint32_t checksum = 0;
/* 0004 */ parray<PSODCGuildCardFileEntry, 100> entries;
/* 3204 */ le_int16_t music_volume = 0;
/* 3206 */ int8_t sound_volume = 0;
/* 3207 */ uint8_t language = 1;
/* 3208 */ le_uint32_t server_time_delta_frames = 540000; // 648000 on DCv1
/* 320C */ le_uint32_t creation_timestamp = 0;
/* 3210 */ le_uint32_t round2_seed = 0;
/* 3214 */
} __packed_ws__(EncryptedSection, 0x3214);
/* 0000 */ EncryptedSection encrypted_section;
/* 3214 */ parray<uint8_t, 0x100> event_flags;
/* 3314 */
} __packed_ws__(PSODCV1V2GuildCardFile, 0x3314);
struct PSOPCGuildCardFile { // PSO______GUD
/* 0000 */ le_uint32_t checksum = 0;
@@ -979,14 +1040,14 @@ std::string decrypt_fixed_size_data_section_s(
size_t size,
uint32_t round1_seed,
bool skip_checksum = false,
uint64_t override_round2_seed = 0xFFFFFFFFFFFFFFFF) {
int64_t override_round2_seed = -1) {
if (size < 2 * sizeof(U32T<BE>)) {
throw std::runtime_error("data size is too small");
}
std::string decrypted = decrypt_data_section<BE>(data_section, size, round1_seed);
uint32_t round2_seed = override_round2_seed < 0x100000000
? static_cast<uint32_t>(override_round2_seed)
uint32_t round2_seed = (override_round2_seed >= 0)
? override_round2_seed
: reinterpret_cast<const U32T<BE>*>(decrypted.data() + decrypted.size() - sizeof(U32T<BE>))->load();
PSOV2Encryption round2_crypt(round2_seed);
if (BE) {
@@ -1011,13 +1072,13 @@ std::string decrypt_fixed_size_data_section_s(
return decrypted;
}
template <typename StructT, bool BE>
template <typename StructT, bool BE, size_t ChecksumLength = sizeof(StructT)>
StructT decrypt_fixed_size_data_section_t(
const void* data_section,
size_t size,
uint32_t round1_seed,
bool skip_checksum = false,
uint64_t override_round2_seed = 0xFFFFFFFFFFFFFFFF) {
int64_t override_round2_seed = -1) {
std::string decrypted = decrypt_data_section<BE>(data_section, size, round1_seed);
if (decrypted.size() < sizeof(StructT)) {
@@ -1025,7 +1086,7 @@ StructT decrypt_fixed_size_data_section_t(
}
StructT ret = *reinterpret_cast<const StructT*>(decrypted.data());
PSOV2Encryption round2_crypt(override_round2_seed < 0x100000000 ? override_round2_seed : ret.round2_seed.load());
PSOV2Encryption round2_crypt((override_round2_seed >= 0) ? override_round2_seed : ret.round2_seed.load());
if (BE) {
round2_crypt.encrypt_big_endian(&ret, offsetof(StructT, round2_seed));
} else {
@@ -1035,7 +1096,7 @@ StructT decrypt_fixed_size_data_section_t(
if (!skip_checksum) {
uint32_t expected_crc = ret.checksum;
ret.checksum = 0;
uint32_t actual_crc = phosg::crc32(&ret, sizeof(ret));
uint32_t actual_crc = phosg::crc32(&ret, ChecksumLength);
ret.checksum = expected_crc;
if (expected_crc != actual_crc) {
throw std::runtime_error(phosg::string_printf(
@@ -1070,12 +1131,12 @@ std::string encrypt_fixed_size_data_section_s(const void* data, size_t size, uin
return encrypt_data_section<BE>(encrypted.data(), encrypted.size(), round1_seed);
}
template <typename StructT, bool BE>
template <typename StructT, bool BE, size_t ChecksumLength = sizeof(StructT)>
std::string encrypt_fixed_size_data_section_t(const StructT& s, uint32_t round1_seed) {
StructT encrypted = s;
encrypted.checksum = 0;
encrypted.round2_seed = phosg::random_object<uint32_t>();
encrypted.checksum = phosg::crc32(&encrypted, sizeof(encrypted));
encrypted.checksum = phosg::crc32(&encrypted, ChecksumLength);
PSOV2Encryption round2_crypt(encrypted.round2_seed);
if (BE) {