From 18aa9191d1d746be70f0f5135b94d1313bb72813 Mon Sep 17 00:00:00 2001 From: quackerd Date: Sat, 11 Apr 2020 00:33:08 -0400 Subject: [PATCH] Initial commit. Co-authored-by: Haoyu Gu --- protobuf/OakProfile.proto | 289 ++++++++++++++++++++++++++++++++++++ protobuf/OakSave.proto | 302 ++++++++++++++++++++++++++++++++++++++ protobuf/OakShared.proto | 47 ++++++ py/bdeh.py | 150 +++++++++++++++++++ 4 files changed, 788 insertions(+) create mode 100644 protobuf/OakProfile.proto create mode 100644 protobuf/OakSave.proto create mode 100644 protobuf/OakShared.proto create mode 100644 py/bdeh.py diff --git a/protobuf/OakProfile.proto b/protobuf/OakProfile.proto new file mode 100644 index 0000000..92ed2d3 --- /dev/null +++ b/protobuf/OakProfile.proto @@ -0,0 +1,289 @@ +package OakSave; +import "OakShared.proto"; +message PlayerInputBinding_Button { + optional string rebind_data_path = 1; + repeated string key_names = 2; +} +message PlayerInputBinding_Axis_Key { + optional string key_name = 1; + optional Vec3 scale_3d = 2; +} +message PlayerInputBinding_Axis { + optional string rebind_data_path = 1; + repeated PlayerInputBinding_Axis_Key keys = 2; +} +message PlayerInputBinding_Category { + optional string category_data_path = 1; + optional string context_data_path = 2; + repeated PlayerInputBinding_Button button_bindings = 3; + repeated PlayerInputBinding_Axis axis_bindings = 4; +} +message PlayerInputBindings { + repeated PlayerInputBinding_Category categories = 1; +} +message OakProfileLastInventoryFilterInfo { + optional string slot_type_id = 1; + optional int32 last_filter_index = 2; +} +message OakProfileMenuTutorialInfo { + repeated string seen_tutorials = 1; + optional bool tutorials_disabled = 2; + optional bool tutorials_allowed_in_non_game_modes = 3; +} +message OakFriendEncounterData { + optional uint32 num_encounters = 1; + optional int64 time_last_encounter = 2; +} +message OakMailItem { + optional uint32 mail_item_type = 1; + optional string sender_display_name = 2; + optional string subject = 3; + optional string body = 4; + optional string gear_serial_number = 5; + optional string mail_guid = 6; + optional int64 date_sent = 7; + optional int64 expiration_date = 8; + optional string from_player_id = 9; + optional bool has_been_read = 10; +} +message GearSoldByFriendData { + optional string gear_serial_number = 1; + optional int32 player_class_identifier_hash = 2; + optional string friend_net_id = 3; +} +message OakCustomizationSaveGameData { + optional bool is_new = 1; + optional string customization_asset_path = 2; +} +message OakInventoryCustomizationPartInfo { + optional uint32 customization_part_hash = 1; + optional bool is_new = 2; +} +message GuardianRankRewardSaveGameData { + optional int32 num_tokens = 1; + optional string reward_data_path = 2; +} +message GuardianRankProfileData { + optional int32 available_tokens = 1; + repeated GuardianRankRewardSaveGameData rank_rewards = 2; + optional int32 guardian_rank = 3; + optional int32 guardian_experience = 4; + optional int32 guardian_reward_random_seed = 5; + optional int64 new_guardian_experience = 6; +} +message CrewQuartersDecorationItemSaveGameData { + optional bool is_new = 1; + optional string decoration_item_asset_path = 2; +} +message CrewQuartersRoomItemSaveGameData { + optional bool is_new = 1; + optional string room_item_asset_path = 2; +} +message RecentlyMetPlayer { + optional string shift_player_id = 1; + optional string first_party_player_id = 2; + optional bool show_shift_player_entry = 3; +} +message Profile { + message FriendEncountersEntry { + optional string key = 1; + optional OakFriendEncounterData value = 2; + } + optional bool enable_aim_assist = 1; + optional bool gamepad_invert_look = 2; + optional bool gamepad_invert_turn = 3; + optional bool gamepad_invert_move = 4; + optional bool gamepad_invert_strafe = 5; + optional bool enable_vibration = 6; + optional bool invert_mouse_pitch = 7; + optional bool enable_mouse_smoothing = 8; + optional float mouse_scale = 9; + optional bool show_damage_numbers = 10; + optional bool show_damage_number_icons = 11; + optional bool enable_training_messages = 12; + optional bool show_text_chat = 13; + optional bool center_crosshair = 14; + optional bool toggle_sprint = 15; + optional bool toggle_crouch = 16; + optional bool censor_content = 17; + optional float music_volume = 18; + optional float sound_effects_volume = 19; + optional float vo_volume = 20; + optional float voice_volume = 21; + optional bool enable_optional_vo = 22; + optional bool push_to_talk = 23; + optional bool enable_controller_audio = 24; + optional float speaker_angle_front = 25; + optional float speaker_angle_side = 26; + optional float speaker_angle_back = 27; + optional uint32 speaker_setup = 28; + optional bool mute_audio_on_focus_loss = 29; + optional bool hide_strict_nat_help_dialog = 34; + optional PlayerInputBindings player_input_bindings = 35; + repeated uint32 news_hashes = 36; + optional uint32 last_used_savegame_id = 37; + optional int32 gamepad_hip_sensitivity_level = 38; + optional int32 gamepad_zoomed_sensitivity_level = 39; + optional int32 gamepad_vehicle_sensitivity_level = 40; + optional float gamepad_movement_dead_zone_x = 41; + optional float gamepad_movement_dead_zone_y = 42; + optional float gamepad_look_dead_zone_inner_x = 43; + optional float gamepad_look_dead_zone_outer_x = 44; + optional float gamepad_look_dead_zone_inner_y = 45; + optional float gamepad_look_dead_zone_outer_y = 46; + optional float gamepad_vehicle_movement_dead_zone_x = 47; + optional float gamepad_vehicle_movement_dead_zone_y = 48; + optional float gamepad_vehicle_look_dead_zone_inner_x = 49; + optional float gamepad_vehicle_look_dead_zone_outer_x = 50; + optional float gamepad_vehicle_look_dead_zone_inner_y = 51; + optional float gamepad_vehicle_look_dead_zone_outer_y = 52; + optional float gamepad_left_dead_zone_inner = 53; + optional float gamepad_left_dead_zone_outer = 54; + optional float gamepad_right_dead_zone_inner = 55; + optional float gamepad_right_dead_zone_outer = 56; + optional float gamepad_look_axial_dead_zone_scale = 57; + optional float gamepad_move_axial_dead_zone_scale = 58; + optional bool gamepad_use_advanced_hip_aim_settings = 59; + optional bool gamepad_use_advanced_zoomed_aim_settings = 60; + optional bool gamepad_use_advanced_vehicle_aim_settings = 61; + optional float gamepad_hip_yaw_rate = 62; + optional float gamepad_hip_pitch_rate = 63; + optional float gamepad_hip_extra_yaw = 64; + optional float gamepad_hip_extra_pitch = 65; + optional float gamepad_hip_ramp_up_time = 66; + optional float gamepad_hip_ramp_up_delay = 67; + optional float gamepad_zoomed_yaw_rate = 68; + optional float gamepad_zoomed_pitch_rate = 69; + optional float gamepad_zoomed_extra_yaw = 70; + optional float gamepad_zoomed_extra_pitch = 71; + optional float gamepad_zoomed_ramp_up_time = 72; + optional float gamepad_zoomed_ramp_up_delay = 73; + optional float gamepad_vehicle_yaw_rate = 74; + optional float gamepad_vehicle_pitch_rate = 75; + optional float gamepad_vehicle_extra_yaw = 76; + optional float gamepad_vehicle_extra_pitch = 77; + optional float gamepad_vehicle_ramp_up_time = 78; + optional float gamepad_vehicle_ramp_up_delay = 79; + optional bool ironsight_aim_assist = 80; + optional uint32 walking_joystick_scheme = 81; + optional uint32 driving_joystick_scheme = 82; + optional float mouse_ads_scale = 83; + optional float mouse_vehicle_scale = 84; + optional bool mouse_ironsight_aim_assist = 85; + optional uint32 vehicle_input_mode = 86; + optional bool weapon_aim_toggle = 87; + optional bool mantle_requires_button = 88; + optional bool fixed_minimap_rotation = 89; + optional bool map_invert_pitch = 90; + optional bool map_invert_yaw = 91; + optional uint32 difficulty = 92; + optional bool swap_dual_wield_controls = 93; + optional float base_fov = 94; + optional uint32 crosshair_neutral_color_frame = 95; + optional uint32 crosshair_enemy_color_frame = 96; + optional uint32 crosshair_ally_color_frame = 97; + optional bool enable_subtitles = 98; + optional bool enable_closed_captions = 99; + optional string last_status_menu_page = 100; + repeated OakProfileLastInventoryFilterInfo inventory_screen_last_filter = 101; + optional OakProfileMenuTutorialInfo tutorial_info = 102; + optional uint32 default_network_type = 103; + optional uint32 default_invite_type = 104; + optional string matchmaking_region = 105; + optional uint32 streaming_service = 106; + optional int32 max_cached_friend_events = 107; + optional int32 max_cached_friend_statuses = 108; + repeated string friend_events = 109; + repeated string friend_statuses = 110; + optional int64 last_whisper_fetch_events_time = 111; + optional int64 last_whisper_fetch_statuses_time = 112; + optional uint32 desired_crossplay_state = 113; + repeated FriendEncountersEntry friend_encounters = 133; + optional int32 max_friend_encounter_size = 134; + repeated GameStatSaveGameData profile_stats_data = 135; + repeated InventoryCategorySaveData bank_inventory_category_list = 136; + repeated bytes bank_inventory_list = 137; + repeated bytes lost_loot_inventory_list = 138; + repeated OakMailItem npc_mail_items = 139; + repeated string mail_guids = 140; + repeated string unread_mail_guids = 141; + repeated GearSoldByFriendData gear_sold_by_friends = 142; + repeated OakSDUSaveGameData profile_sdu_list = 143; + repeated OakCustomizationSaveGameData unlocked_customizations = 144; + repeated OakInventoryCustomizationPartInfo unlocked_inventory_customization_parts = 145; + optional GuardianRankProfileData guardian_rank = 146; + repeated CrewQuartersDecorationItemSaveGameData unlocked_crew_quarters_decorations = 147; + repeated CrewQuartersRoomItemSaveGameData unlocked_crew_quarters_rooms = 148; + optional bool enable_mouse_acceleration = 150; + optional bool enable_gamepad_input = 151; + optional bool use_classic_gamepad_input = 152; + optional float master_volume = 153; + optional uint32 monitor_display_type = 154; + optional uint32 graphics_mode = 155; + optional uint32 frame_rate_limit = 156; + optional float base_vehicle_fov = 157; + optional uint32 graphics_quality = 158; + optional uint32 anisotropic_filtering = 159; + optional uint32 shadow_quality = 160; + optional uint32 display_performance_stats = 161; + optional uint32 texture_detail = 162; + optional uint32 draw_distance = 163; + optional uint32 clutter = 164; + optional uint32 tessellation = 165; + optional uint32 foliage = 166; + optional bool foliage_shadows = 167; + optional bool planar_reflections = 168; + optional uint32 volumetric_fog = 169; + optional uint32 screen_space_reflections = 170; + optional uint32 character_texture_detail = 171; + optional uint32 character_detail = 172; + optional uint32 ambient_occlusion_quality = 173; + optional bool object_motion_blur = 174; + optional bool lens_flare = 175; + optional bool combat_number_long_format = 176; + optional bool show_minimap_legendaries = 177; + optional bool use_player_callouts = 178; + optional uint32 friend_event_notification_lifetime = 179; + optional uint32 friend_event_notification_frequency = 180; + optional uint32 trade_request_reception_type = 181; + optional float head_bob_scale = 182; + optional bool has_seen_first_boot = 184; + optional float subs_cc_size = 189; + optional float cc_subs_background_opacity = 190; + optional uint32 walking_button_scheme = 191; + optional uint32 driving_button_scheme = 192; + optional uint32 glyph_mode = 193; + optional bool use_MPH = 194; + repeated RegisteredDownloadableEntitlements registered_downloadable_entitlements = 195; + repeated string seen_news_items = 196; + optional bool auto_centering_enabled = 197; + optional bool increased_chance_for_subscribers = 198; + optional bool rare_chest_event_enabled = 199; + optional bool badass_event_enabled = 200; + optional bool pinata_event_enabled = 201; + optional int32 min_time_between_badass_events = 202; + optional float hud_scale_multiplier = 203; + optional bool disable_spatial_audio = 204; + optional int32 total_playtime_seconds = 205; + optional bool moxxis_drink_event_enabled = 206; + optional int32 moxxis_drink_event_bits_product_id = 207; + repeated ChallengeSaveGameData challenge_data = 208; + repeated int32 CitizenScienceLevelProgression = 209; + optional bool disable_event_content = 211; + optional uint32 desired_friend_sync_state = 212; + optional bool needs_shift_first_boot = 213; + repeated RecentlyMetPlayer recently_met_players = 214; + optional int32 CitizenScienceActiveBoosterIndex = 215; + optional float CitizenScienceActiveBoosterRemainingTime = 216; + optional float CitizenScienceActiveBoosterTotalTime = 217; + optional int32 StreamerPrimaryActiveBoosterIndex = 218; + optional float StreamerPrimaryActiveBoosterRemainingTime = 219; + optional float StreamerPrimaryActiveBoosterTotalTime = 220; + optional int32 StreamerSecondaryActiveBoosterIndex = 221; + optional float StreamerSecondaryActiveBoosterRemainingTime = 222; + optional float StreamerSecondaryActiveBoosterTotalTime = 223; + optional int32 StreamerBoosterTier = 224; + optional int32 CitizenScienceCSBucksAmount = 226; + optional bool bCitizenScienceHasSeenIntroVideo = 227; + optional bool bCitizenScienceTutorialDone = 228; +} diff --git a/protobuf/OakSave.proto b/protobuf/OakSave.proto new file mode 100644 index 0000000..56e9bd7 --- /dev/null +++ b/protobuf/OakSave.proto @@ -0,0 +1,302 @@ +syntax = "proto2"; +package OakSave; +import "OakShared.proto"; +message PlayerClassSaveGameData { + optional string player_class_path = 1; + optional uint32 dlc_package_id = 2; +} +message ResourcePoolSavegameData { + optional float amount = 1; + optional string resource_path = 2; +} +message RegionSaveGameData { + optional int32 game_stage = 1; + optional int32 play_through_idx = 2; + optional string region_path = 3; + optional uint32 dlc_package_id = 4; +} +message InventoryBalanceStateInitializationData { + optional int32 game_stage = 1; + optional string inventory_data = 2; + optional string inventory_balance_data = 3; + optional string manufacturer_data = 4; + repeated string part_list = 5; + repeated string generic_part_list = 6; + optional bytes additional_data = 7; + repeated string customization_part_list = 8; +} +message OakInventoryItemSaveGameData { + optional bytes item_serial_number = 1; + optional int32 pickup_order_index = 2; + optional int32 flags = 3; + optional string weapon_skin_path = 4; + optional InventoryBalanceStateInitializationData development_save_data = 5; +} +message EquippedInventorySaveGameData { + optional int32 inventory_list_index = 1; + optional bool enabled = 2; + optional string slot_data_path = 3; + optional string trinket_data_path = 4; +} +message OakAbilityTreeItemSaveGameData { + optional string item_asset_path = 1; + optional int32 points = 2; + optional int32 max_points = 3; + optional int32 tree_identifier = 4; +} +message OakAbilitySlotSaveGameData { + optional string ability_class_path = 1; + optional string slot_asset_path = 2; +} +message OakActionAbilityAugmentSaveGameData { + optional string action_ability_class_path = 1; + optional string slot_asset_path = 2; + optional string augment_asset_path = 3; +} +message OakActionAbilityAugmentConfigurationSaveGameData { + optional string ability_class_path = 1; + optional string augment_asset_path = 2; + optional string mod_slot_asset_path = 3; + optional string mod_asset_path = 4; +} +message OakPlayerAbilitySaveGameData { + optional int32 ability_points = 1; + repeated OakAbilityTreeItemSaveGameData tree_item_list = 2; + repeated OakAbilitySlotSaveGameData ability_slot_list = 3; + repeated OakActionAbilityAugmentSaveGameData augment_slot_list = 4; + repeated OakActionAbilityAugmentConfigurationSaveGameData augment_configuration_list = 5; + optional int32 tree_grade = 6; +} +message MissionStatusPlayerSaveGameData { + enum MissionState { + MS_NotStarted = 0; + MS_Active = 1; + MS_Complete = 2; + MS_Failed = 3; + MS_Unknown = 4; + } + optional MissionState status = 1; + optional bool has_been_viewed_in_log = 2; + repeated int32 objectives_progress = 3; + optional string mission_class_path = 4; + optional string active_objective_set_path = 5; + optional uint32 dlc_package_id = 6; + optional bool kickoff_played = 7; + optional uint32 league_instance = 8; +} +message MissionPlaythroughSaveGameData { + repeated MissionStatusPlayerSaveGameData mission_list = 1; + optional string tracked_mission_class_path = 2; +} +message ActiveFastTravelSaveData { + optional string active_travel_station_name = 1; + optional bool blacklisted = 2; +} +message PlaythroughActiveFastTravelSaveData { + repeated ActiveFastTravelSaveData active_travel_stations = 1; +} +message DiscoveredAreaInfo { + optional string discovered_area_name = 1; + optional uint32 discovered_playthroughs = 2; +} +message DiscoveredLevelInfo { + optional string discovered_level_name = 1; + optional uint32 discovered_playthroughs = 3; + repeated DiscoveredAreaInfo discovered_area_info = 4; +} +message DiscoveredPlanetInfo { + optional string discovered_planet = 1; + optional bool is_new_planet = 2; +} +message DiscoverySaveData { + repeated DiscoveredLevelInfo discovered_level_info = 1; +} +message VehicleUnlockedSaveGameData { + optional string asset_path = 1; + optional bool just_unlocked = 2; +} +message OakCARMenuVehicleConfigSaveData { + optional string loadout_save_name = 1; + optional string body_asset_path = 2; + optional string wheel_asset_path = 3; + optional string armor_asset_path = 4; + optional string core_mod_asset_path = 5; + optional string gunner_weapon_asset_path = 6; + optional string driver_weapon_asset_path = 7; + optional string ornament_asset_path = 8; + optional string material_decal_asset_path = 9; + optional string material_asset_path = 10; + optional int32 color_index_1 = 11; + optional int32 color_index_2 = 12; + optional int32 color_index_3 = 13; +} +message CustomPlayerColorSaveGameData { + optional string color_parameter = 1; + optional Vec3 applied_color = 2; + optional Vec3 split_color = 3; + optional bool use_default_color = 4; + optional bool use_default_split_color = 5; +} +message GuardianRankSaveGameData { + optional int32 guardian_rank = 1; + optional int32 guardian_experience = 2; +} +message GuardianRankRewardCharacterSaveGameData { + optional int32 num_tokens = 1; + optional bool is_enabled = 2; + optional string reward_data_path = 3; +} +message GuardianRankPerkCharacterSaveGameData { + optional bool is_enabled = 1; + optional string perk_data_path = 2; +} +message GuardianRankCharacterSaveGameData { + optional int32 guardian_available_tokens = 1; + optional int32 guardian_rank = 2; + optional int32 guardian_experience = 3; + repeated GuardianRankRewardCharacterSaveGameData rank_rewards = 4; + repeated GuardianRankPerkCharacterSaveGameData rank_perks = 5; + optional int32 guardian_reward_random_seed = 6; + optional int64 new_guardian_experience = 7; + optional bool is_rank_system_enabled = 8; +} +message CrewQuartersDecorationSaveData { + optional int32 decoration_index = 1; + optional string decoration_data_path = 2; +} +message CrewQuartersSaveData { + optional int32 preferred_room_assignment = 1; + repeated CrewQuartersDecorationSaveData decorations = 2; + optional string room_data_path = 3; +} +message CrewQuartersGunRackItemSaveData { + optional bytes encrypted_serial_number = 1; + optional string slot_asset_path = 2; + optional InventoryBalanceStateInitializationData development_save_data = 3; +} +message CrewQuartersGunRackSaveData { + repeated CrewQuartersGunRackItemSaveData rack_save_data = 1; +} +message EchoLogSaveGameData { + optional bool has_been_seen_in_log = 1; + optional string echo_log_path = 2; +} +message MapIDData { + optional uint32 zone_name_id = 1; + optional uint32 map_name_id = 2; +} +message GameStateSaveData { + optional MapIDData last_traveled_map_id = 1; + optional int32 mayhem_level = 2; +} +message ChallengeCategoryProgressSaveData { + optional bytes category_progress = 1; +} +message OakPlayerCharacterAugmentSaveGameData { + optional string slot_asset_path = 1; + optional string augment_asset_path = 2; +} +message OakPlayerCharacterSlotSaveGameData { + repeated OakPlayerCharacterAugmentSaveGameData augment_slot_list = 1; +} +message UITrackingSaveGameData { + optional bool has_seen_skill_menu_unlock = 1; + optional bool has_seen_guardian_rank_menu_unlock = 2; + optional bool has_seen_echo_boot_ammo_bar = 3; + optional bool has_seen_echo_boot_shield_bar = 4; + optional bool has_seen_echo_boot_grenades = 5; + optional int32 highest_thvm_breadcrumb_seen = 6; + repeated string inventory_slot_unlocks_seen = 7; + optional int32 saved_spin_offset = 8; +} +message PlanetCycleInfo { + optional string planet_name = 1; + optional float cycle_length = 2; + optional float last_cached_time = 3; +} +message TimeOfDaySaveGameData { + repeated PlanetCycleInfo planet_cycle_info = 1; + optional string planet_cycle = 2; +} +message LevelPersistence_Actor_SaveGameData { + optional string actor_name = 1; + optional int32 timer_remaining = 2; +} +message LevelPersistence_Level_SaveGameData { + optional string level_name = 1; + repeated LevelPersistence_Actor_SaveGameData saved_actors = 2; +} +message GbxZoneMapFODSavedLevelData { + optional string level_name = 1; + optional uint32 fod_texture_size = 2; + optional uint32 num_chunks = 3; + optional float discovery_percentage = 4; + optional uint32 data_state = 5; + optional uint32 data_revision = 6; + optional bytes fod_data = 7; +} +message GbxZoneMapFODSaveGameData { + repeated GbxZoneMapFODSavedLevelData level_data = 1; +} +message Character { + message NicknameMappingsEntry { + optional string key = 1; + optional string value = 2; + } + optional uint32 save_game_id = 1; + optional int64 last_save_timestamp = 2; + optional uint32 time_played_seconds = 3; + optional PlayerClassSaveGameData player_class_data = 4; + repeated ResourcePoolSavegameData resource_pools = 5; + repeated RegionSaveGameData saved_regions = 6; + optional int32 experience_points = 7; + repeated GameStatSaveGameData game_stats_data = 8; + repeated InventoryCategorySaveData inventory_category_list = 9; + repeated OakInventoryItemSaveGameData inventory_items = 10; + repeated EquippedInventorySaveGameData equipped_inventory_list = 11; + repeated int32 active_weapon_list = 12; + optional OakPlayerAbilitySaveGameData ability_data = 13; + optional int32 last_play_through_index = 14; + optional int32 playthroughs_completed = 15; + optional bool show_new_playthrough_notification = 16; + repeated MissionPlaythroughSaveGameData mission_playthroughs_data = 17; + repeated string active_travel_stations = 21; + optional DiscoverySaveData discovery_data = 22; + optional string last_active_travel_station = 23; + repeated VehicleUnlockedSaveGameData vehicles_unlocked_data = 24; + repeated string vehicle_parts_unlocked = 25; + repeated OakCARMenuVehicleConfigSaveData vehicle_loadouts = 26; + optional int32 vehicle_last_loadout_index = 27; + repeated ChallengeSaveGameData challenge_data = 28; + repeated OakSDUSaveGameData sdu_list = 29; + repeated string selected_customizations = 30; + repeated int32 equipped_emote_customizations = 31; + repeated CustomPlayerColorSaveGameData selected_color_customizations = 32; + optional GuardianRankSaveGameData guardian_rank = 33; + optional CrewQuartersSaveData crew_quarters_room = 34; + optional CrewQuartersGunRackSaveData crew_quarters_gun_rack = 35; + repeated EchoLogSaveGameData unlocked_echo_logs = 36; + optional bool has_played_special_echo_log_insert_already = 37; + repeated NicknameMappingsEntry nickname_mappings = 38; + optional MapIDData last_traveled_map_id = 39; + optional ChallengeCategoryProgressSaveData challenge_category_completion_pcts = 40; + optional OakPlayerCharacterSlotSaveGameData character_slot_save_game_data = 41; + optional UITrackingSaveGameData ui_tracking_save_game_data = 42; + optional string preferred_character_name = 43; + optional int32 name_character_limit = 44; + optional uint32 preferred_group_mode = 45; + optional TimeOfDaySaveGameData time_of_day_save_game_data = 46; + repeated LevelPersistence_Level_SaveGameData level_persistence_data = 47; + optional uint32 accumulated_level_persistence_reset_timer_seconds = 48; + optional uint32 mayhem_level = 49; + optional GbxZoneMapFODSaveGameData gbx_zone_map_fod_save_game_data = 50; + repeated ActiveFastTravelSaveData active_or_blacklisted_travel_stations = 51; + repeated string last_active_travel_station_for_playthrough = 52; + repeated GameStateSaveData game_state_save_data_for_playthrough = 53; + repeated RegisteredDownloadableEntitlements registered_downloadable_entitlements = 54; + repeated PlaythroughActiveFastTravelSaveData active_travel_stations_for_playthrough = 55; + optional string save_game_guid = 56; + optional GuardianRankCharacterSaveGameData guardian_rank_character_data = 57; + optional bool optional_objective_reward_fixup_applied = 58; + optional bool vehicle_part_rewards_fixup_applied = 59; +} diff --git a/protobuf/OakShared.proto b/protobuf/OakShared.proto new file mode 100644 index 0000000..42174f8 --- /dev/null +++ b/protobuf/OakShared.proto @@ -0,0 +1,47 @@ +syntax = "proto2"; +package OakSave; +message Vec3 { + optional float x = 1; + optional float y = 2; + optional float z = 3; +} +message GameStatSaveGameData { + optional int32 stat_value = 1; + optional string stat_path = 2; +} +message InventoryCategorySaveData { + optional uint32 base_category_definition_hash = 1; + optional int32 quantity = 2; +} +message OakSDUSaveGameData { + optional int32 sdu_level = 1; + optional string sdu_data_path = 2; +} +message RegisteredDownloadableEntitlement { + optional int32 id = 1; + optional uint32 consumed = 2; + optional bool registered = 3; + optional bool seen = 4; +} +message RegisteredDownloadableEntitlements { + optional string entitlement_source_asset_path = 1; + repeated int64 entitlement_ids = 2; + repeated RegisteredDownloadableEntitlement entitlements = 3; +} +message ChallengeStatSaveGameData { + optional int32 current_stat_value = 1; + optional string challenge_stat_path = 2; +} +message OakChallengeRewardSaveGameData { + optional bool challenge_reward_claimed = 1; +} +message ChallengeSaveGameData { + optional int32 completed_count = 1; + optional bool is_active = 2; + optional bool currently_completed = 3; + optional int32 completed_progress_level = 4; + optional int32 progress_counter = 5; + repeated ChallengeStatSaveGameData stat_instance_state = 6; + optional string challenge_class_path = 7; + repeated OakChallengeRewardSaveGameData challenge_reward_info = 8; +} diff --git a/py/bdeh.py b/py/bdeh.py new file mode 100644 index 0000000..0e9e17c --- /dev/null +++ b/py/bdeh.py @@ -0,0 +1,150 @@ +import os +import sys +import OakSave_pb2 + +dk1 = bytearray([0x71, 0x34, 0x36, 0xB3, 0x56, 0x63, 0x25, 0x5F, 0xEA, 0xE2, 0x83, 0x73, 0xF4, 0x98, 0xB8, 0x18, 0x2E, 0xE5, 0x42, 0x2E, 0x50, 0xA2, 0x0F, 0x49, 0x87, 0x24, 0xE6, 0x65, 0x9A, 0xF0, 0x7C, 0xD7]) +dk2 = bytearray([0x7C, 0x07, 0x69, 0x83, 0x31, 0x7E, 0x0C, 0x82, 0x5F, 0x2E, 0x36, 0x7F, 0x76, 0xB4, 0xA2, 0x71, 0x38, 0x2B, 0x6E, 0x87, 0x39, 0x05, 0x02, 0xC6, 0xCD, 0xD8, 0xB1, 0xCC, 0xA1, 0x33, 0xF9, 0xB6]) + +def write_u32_le(sc : bytearray, offset : int, val: int): + sc[offset] = val & 0xFF + sc[offset + 1] = (val >> 8) & 0xFF + sc[offset + 2] = (val >> 16) & 0xFF + sc[offset + 3] = (val >> 24) & 0xFF + +def read_u32_le(sc : bytearray, offset : int): + ret = sc[offset] + ret |= sc[offset + 1] << 8 + ret |= sc[offset + 2] << 16 + ret |= sc[offset + 3] << 24 + return ret + +def find_save_core_offset(sc : bytearray): + #core offset is always "OakSaveGame\0" + 4 bytes (<- this is the payload size) + sc_str = "OakSaveGame" + ret = -1 + for offset in range(0, len(sc) - len(sc_str)): + match = True + for i in range(0, len(sc_str)): + if sc[offset + i] != ord(sc_str[i]): + match = False + break + if match: + ret = offset + break + + if ret == -1: + print("Cannot locate core offset") + exit(-1) + + # skip the OakSaveGame string + ret = ret + len(sc_str) + 1 + core_sz = read_u32_le(sc, ret) + return ret, core_sz + +def usage(): + print("Usage: python bdeh.py ") + + +def decrypt(src : bytearray): + offset = len(src) - 1 + while offset >= 0: + k1 = 0 + if offset >= 0x20: + k1 = src[offset - 0x20] + else: + k1 = dk1[offset] + + k2 = offset + k2 = k2 % 0x20 + k2 = dk2[k2] + k2 = k2 ^ k1 + + src[offset] = int(src[offset]) ^ k2 + offset = offset - 1 + return src + +def encrypt(src : bytearray): + offset = 0 + while offset < len(src): + if offset >= 0x20: + k1 = src[offset - 0x20] + else: + k1 = dk1[offset] + + k2 = offset + k2 = k2 % 0x20 + k2 = dk2[k2] + k2 = k2 ^ k1 + + src[offset] = int(src[offset]) ^ k2 + offset = offset + 1 + return src + +def editor(save_obj): + return + +def main(): + if len(sys.argv) != 2: + usage() + exit() + + print("Reading " + sys.argv[1]) + with open(sys.argv[1], "rb") as f: + savfile = f.read() + + core_offset, core_sz = find_save_core_offset(savfile) + + if core_offset + core_sz + 4 != len(savfile): + print("Incorrect file size!") + exit(1) + + print("Save file loaded - size: " + hex(len(savfile)) + " core offset: " + hex(core_offset) + " core size: " + hex(core_sz)) + + # copy the payload + payload = bytearray(core_sz) + + for i in range(0, core_sz): + payload[i] = savfile[core_offset + 4 + i] + + # copy the header + header = bytearray(core_offset) + + for i in range(0, core_offset): + header[i] = savfile[i] + + # garbage collect the savfile buffer + savfile = None + + # decrypt the payload + payload = decrypt(payload) + + # deserialize the payload + save_obj = OakSave_pb2.Character() + save_obj.ParseFromString(payload) + + # editor loop + editor(save_obj) + + # serialize the payload + payload_ro = save_obj.SerializeToString() + + payload = bytearray(len(payload_ro)) + for i in range(0, len(payload_ro)): + payload[i] = payload_ro[i] + + # encrypt the payload + payload = encrypt(payload) + + # build the full save + full_save = bytearray(core_offset + 4 + len(payload)) + for i in range(0, core_offset): + full_save[i] = header[i] + write_u32_le(full_save, core_offset, len(payload)) + for i in range(0, len(payload)): + full_save[core_offset + 4 + i] = payload[i] + + # Write + with open(sys.argv[1] + ".bdeh", "wb") as f: + f.write(full_save) + +main()