diff --git a/core/data/alembic/versions/a616fd164e40_sao_backport.py b/core/data/alembic/versions/a616fd164e40_sao_backport.py new file mode 100644 index 0000000..c4b885b --- /dev/null +++ b/core/data/alembic/versions/a616fd164e40_sao_backport.py @@ -0,0 +1,723 @@ +"""sao_backport + +Revision ID: a616fd164e40 +Revises: 48f4acc43a7e +Create Date: 2024-06-24 20:28:34.471282 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'a616fd164e40' +down_revision = '48f4acc43a7e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index('sao_static_quest_uk', table_name='sao_static_quest') + op.drop_column('sao_static_quest', 'questSceneId') + op.drop_column('sao_static_quest', 'name') + op.drop_column('sao_static_quest', 'sortNo') + op.drop_column('sao_static_quest', 'id') + op.drop_column('sao_static_quest', 'enabled') + op.add_column('sao_static_quest', sa.Column('QuestSceneId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('SortNo', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('Tutorial', sa.BOOLEAN(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('ColRate', sa.DECIMAL(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('LimitDefault', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('LimitResurrection', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('RewardTableSubId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('PlayerTraceTableSubId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('SuccessPlayerExp', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('FailedPlayerExp', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('PairExpRate', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('TrioExpRate', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('SingleRewardVp', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('PairRewardVp', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_quest', sa.Column('TrioRewardVp', sa.INTEGER(), nullable=False)) + op.create_primary_key(None, "sao_static_quest", ['QuestSceneId']) + op.drop_column('sao_static_quest', 'version') + op.create_table('sao_player_resource_card', + sa.Column('id', sa.BIGINT(), nullable=False), + sa.Column('user', sa.INTEGER(), nullable=False), + sa.Column('common_reward_type', sa.INTEGER(), nullable=False), + sa.Column('common_reward_id', sa.INTEGER(), nullable=False), + sa.Column('holographic_flag', sa.BOOLEAN(), server_default='0', nullable=False), + sa.Column('serial', sa.VARCHAR(length=20), nullable=True), + sa.ForeignKeyConstraint(['user'], ['aime_user.id'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('serial'), + mysql_charset='utf8mb4' + ) + op.create_table('sao_player_tutorial', + sa.Column('id', sa.BIGINT(), nullable=False), + sa.Column('user', sa.INTEGER(), nullable=False), + sa.Column('tutorial_byte', sa.INTEGER(), nullable=False), + sa.ForeignKeyConstraint(['user'], ['aime_user.id'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('user', 'tutorial_byte', name='sao_player_tutorial_uk'), + mysql_charset='utf8mb4' + ) + op.create_table('sao_static_episode', + sa.Column('EpisodeId', sa.BIGINT(), nullable=False), + sa.Column('EpisodeChapterId', sa.INTEGER(), nullable=False), + sa.Column('ReleaseEpisodeId', sa.INTEGER(), nullable=False), + sa.Column('Title', sa.VARCHAR(length=255), nullable=False), + sa.Column('CommentSummary', sa.VARCHAR(length=255), nullable=False), + sa.Column('ExBonusTableSubId', sa.INTEGER(), nullable=False), + sa.Column('QuestSceneId', sa.BIGINT(), nullable=True), + sa.ForeignKeyConstraint(['QuestSceneId'], ['sao_static_quest.QuestSceneId'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('EpisodeId'), + mysql_charset='utf8mb4' + ) + op.create_table('sao_static_ex_bonus', + sa.Column('ExBonusTableId', sa.BIGINT(), nullable=False), + sa.Column('ExBonusTableSubId', sa.INTEGER(), nullable=False), + sa.Column('ExBonusConditionId', sa.INTEGER(), nullable=False), + sa.Column('ConditionValue1', sa.INTEGER(), nullable=False), + sa.Column('ConditionValue2', sa.INTEGER(), nullable=False), + sa.Column('CommonRewardType', sa.INTEGER(), nullable=False), + sa.Column('CommonRewardId', sa.INTEGER(), nullable=False), + sa.Column('CommonRewardNum', sa.INTEGER(), nullable=False), + sa.Column('Strength', sa.INTEGER(), nullable=False), + sa.Column('Property1PropertyId', sa.BIGINT(), nullable=False), + sa.Column('Property1Value1', sa.INTEGER(), nullable=False), + sa.Column('Property1Value2', sa.INTEGER(), nullable=False), + sa.Column('Property2PropertyId', sa.BIGINT(), nullable=False), + sa.Column('Property2Value1', sa.INTEGER(), nullable=False), + sa.Column('Property2Value2', sa.INTEGER(), nullable=False), + sa.Column('Property3PropertyId', sa.BIGINT(), nullable=False), + sa.Column('Property3Value1', sa.INTEGER(), nullable=False), + sa.Column('Property3Value2', sa.INTEGER(), nullable=False), + sa.Column('Property4PropertyId', sa.BIGINT(), nullable=False), + sa.Column('Property4Value1', sa.INTEGER(), nullable=False), + sa.Column('Property4Value2', sa.INTEGER(), nullable=False), + sa.ForeignKeyConstraint(['Property1PropertyId'], ['sao_static_property.PropertyId'], onupdate='cascade', ondelete='cascade'), + sa.ForeignKeyConstraint(['Property2PropertyId'], ['sao_static_property.PropertyId'], onupdate='cascade', ondelete='cascade'), + sa.ForeignKeyConstraint(['Property3PropertyId'], ['sao_static_property.PropertyId'], onupdate='cascade', ondelete='cascade'), + sa.ForeignKeyConstraint(['Property4PropertyId'], ['sao_static_property.PropertyId'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('ExBonusTableId'), + mysql_charset='utf8mb4' + ) + op.create_table('sao_static_ex_tower', + sa.Column('ExTowerQuestId', sa.BIGINT(), nullable=False), + sa.Column('ExTowerId', sa.INTEGER(), nullable=False), + sa.Column('ReleaseExTowerQuestId', sa.INTEGER(), nullable=False), + sa.Column('Title', sa.VARCHAR(length=255), nullable=False), + sa.Column('Title_en', sa.VARCHAR(length=255), nullable=True), + sa.Column('ExBonusTableSubId', sa.INTEGER(), nullable=False), + sa.Column('QuestSceneId', sa.BIGINT(), nullable=False), + sa.ForeignKeyConstraint(['QuestSceneId'], ['sao_static_quest.QuestSceneId'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('ExTowerQuestId'), + mysql_charset='utf8mb4' + ) + op.create_table('sao_static_side_quest', + sa.Column('SideQuestId', sa.BIGINT(), nullable=False), + sa.Column('DisplayName', sa.VARCHAR(length=255), nullable=False), + sa.Column('DisplayName_en', sa.VARCHAR(length=255), nullable=True), + sa.Column('EpisodeNum', sa.INTEGER(), nullable=False), + sa.Column('ExBonusTableSubId', sa.INTEGER(), nullable=False), + sa.Column('QuestSceneId', sa.BIGINT(), nullable=False), + sa.ForeignKeyConstraint(['QuestSceneId'], ['sao_static_quest.QuestSceneId'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('SideQuestId'), + sa.UniqueConstraint('SideQuestId'), + mysql_charset='utf8mb4' + ) + op.create_table('sao_static_skill_table', + sa.Column('SkillTableId', sa.BIGINT(), nullable=False), + sa.Column('SkillId', sa.BIGINT(), nullable=False), + sa.Column('SkillTableSubId', sa.INTEGER(), nullable=False), + sa.Column('LevelObtained', sa.INTEGER(), nullable=False), + sa.Column('AwakeningId', sa.INTEGER(), nullable=False), + sa.ForeignKeyConstraint(['SkillId'], ['sao_static_skill.SkillId'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('SkillTableId'), + mysql_charset='utf8mb4' + ) + op.create_table('sao_static_tower', + sa.Column('TowerId', sa.BIGINT(), nullable=False), + sa.Column('ReleaseTowerId', sa.INTEGER(), nullable=False), + sa.Column('ExBonusTableSubId', sa.INTEGER(), nullable=False), + sa.Column('QuestSceneId', sa.BIGINT(), nullable=False), + sa.ForeignKeyConstraint(['QuestSceneId'], ['sao_static_quest.QuestSceneId'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('TowerId'), + mysql_charset='utf8mb4' + ) + op.create_table('sao_player_ex_bonus', + sa.Column('id', sa.BIGINT(), nullable=False), + sa.Column('user', sa.INTEGER(), nullable=False), + sa.Column('quest_scene_id', sa.BIGINT(), nullable=False), + sa.Column('ex_bonus_table_id', sa.BIGINT(), nullable=False), + sa.Column('quest_clear_flag', sa.BOOLEAN(), server_default='0', nullable=False), + sa.ForeignKeyConstraint(['ex_bonus_table_id'], ['sao_static_ex_bonus.ExBonusTableId'], onupdate='cascade', ondelete='cascade'), + sa.ForeignKeyConstraint(['quest_scene_id'], ['sao_static_quest.QuestSceneId'], onupdate='cascade', ondelete='cascade'), + sa.ForeignKeyConstraint(['user'], ['aime_user.id'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('user', 'quest_scene_id', 'ex_bonus_table_id', name='sao_player_ex_bonus_uk'), + mysql_charset='utf8mb4' + ) + op.alter_column('sao_end_sessions', 'play_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('now()'), + existing_nullable=False) + op.alter_column("sao_equipment_data", "equipment_id", existing_type=sa.Integer(), type_=sa.BIGINT()) + op.add_column('sao_equipment_data', sa.Column('is_shop_purchase', sa.BOOLEAN(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('is_protect', sa.BOOLEAN(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property1_property_id', sa.BIGINT(), server_default='2', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property1_value1', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property1_value2', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property2_property_id', sa.BIGINT(), server_default='2', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property2_value1', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property2_value2', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property3_property_id', sa.BIGINT(), server_default='2', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property3_value1', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property3_value2', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property4_property_id', sa.BIGINT(), server_default='2', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property4_value1', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('property4_value2', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_equipment_data', sa.Column('converted_card_num', sa.INTEGER(), server_default='0', nullable=False)) + op.alter_column('sao_equipment_data', 'get_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('now()'), + existing_nullable=False) + op.drop_column('sao_static_equipment_list', 'id') + op.drop_column('sao_static_equipment_list', 'name') + op.drop_column('sao_static_equipment_list', 'flavorText') + op.drop_column('sao_static_equipment_list', 'equipmentId') + op.drop_column('sao_static_equipment_list', 'rarity') + op.drop_column('sao_static_equipment_list', 'enabled') + op.drop_column('sao_static_equipment_list', 'weaponTypeId') + op.drop_column('sao_static_equipment_list', 'equipmentType') + op.add_column('sao_static_equipment_list', sa.Column('EquipmentId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('EquipmentType', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('WeaponTypeId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Name', sa.VARCHAR(length=255), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Name_en', sa.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_equipment_list', sa.Column('Rarity', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Power', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('StrengthIncrement', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('SkillCondition', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property1PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property1Value1', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property1Value2', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property2PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property2Value1', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property2Value2', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property3PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property3Value1', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property3Value2', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property4PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property4Value1', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('Property4Value2', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('SalePrice', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('CompositionExp', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('AwakeningExp', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('FlavorText', sa.VARCHAR(length=255), nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('FlavorText_en', sa.VARCHAR(length=255), nullable=True)) + op.create_primary_key(None, "sao_static_equipment_list", ['EquipmentId']) + op.drop_index('sao_static_equipment_list_uk', table_name='sao_static_equipment_list') + op.create_foreign_key(None, 'sao_static_equipment_list', 'sao_static_property', ['Property1PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_static_equipment_list', 'sao_static_property', ['Property2PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_static_equipment_list', 'sao_static_property', ['Property3PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_static_equipment_list', 'sao_static_property', ['Property4PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.drop_column('sao_static_equipment_list', 'version') + op.create_foreign_key(None, 'sao_equipment_data', 'sao_static_equipment_list', ['equipment_id'], ['EquipmentId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_equipment_data', 'sao_static_property', ['property1_property_id'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_equipment_data', 'sao_static_property', ['property4_property_id'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_equipment_data', 'sao_static_property', ['property2_property_id'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_equipment_data', 'sao_static_property', ['property3_property_id'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.add_column('sao_hero_log_data', sa.Column('hero_log_id', sa.BIGINT(), nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('max_level_extend_num', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('is_awakenable', sa.BOOLEAN(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('awakening_stage', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('awakening_exp', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('is_shop_purchase', sa.BOOLEAN(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('is_protect', sa.BOOLEAN(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property1_property_id', sa.BIGINT(), server_default='2', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property1_value1', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property1_value2', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property2_property_id', sa.BIGINT(), server_default='2', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property2_value1', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property2_value2', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property3_property_id', sa.BIGINT(), server_default='2', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property3_value1', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property3_value2', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property4_property_id', sa.BIGINT(), server_default='2', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property4_value1', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('property4_value2', sa.INTEGER(), server_default='0', nullable=False)) + op.add_column('sao_hero_log_data', sa.Column('converted_card_num', sa.INTEGER(), server_default='0', nullable=False)) + op.alter_column('sao_hero_log_data', 'main_weapon', + existing_type=mysql.INTEGER(display_width=11), + nullable=True) + op.alter_column('sao_hero_log_data', 'sub_equipment', + existing_type=mysql.INTEGER(display_width=11), + nullable=True) + op.alter_column('sao_hero_log_data', 'skill_slot1_skill_id', + existing_type=mysql.INTEGER(display_width=11), + type_=sa.BIGINT(), + nullable=True) + op.alter_column('sao_hero_log_data', 'skill_slot2_skill_id', + existing_type=mysql.INTEGER(display_width=11), + type_=sa.BIGINT(), + nullable=True) + op.alter_column('sao_hero_log_data', 'skill_slot3_skill_id', + existing_type=mysql.INTEGER(display_width=11), + type_=sa.BIGINT(), + nullable=True) + op.alter_column('sao_hero_log_data', 'skill_slot4_skill_id', + existing_type=mysql.INTEGER(display_width=11), + type_=sa.BIGINT(), + nullable=True) + op.alter_column('sao_hero_log_data', 'skill_slot5_skill_id', + existing_type=mysql.INTEGER(display_width=11), + type_=sa.BIGINT(), + nullable=True) + op.alter_column('sao_hero_log_data', 'get_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('now()'), + existing_nullable=False) + op.drop_constraint('sao_hero_log_data_ibfk_1', 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint('sao_hero_log_data_uk', 'sao_hero_log_data', type_='unique') + op.create_unique_constraint('sao_hero_log_data_uk', 'sao_hero_log_data', ['user', 'hero_log_id']) + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_property', ['property2_property_id'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_skill', ['skill_slot2_skill_id'], ['SkillId'], onupdate='set null', ondelete='set null') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_property', ['property1_property_id'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_skill', ['skill_slot1_skill_id'], ['SkillId'], onupdate='set null', ondelete='set null') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_equipment_data', ['main_weapon'], ['id'], onupdate='set null', ondelete='set null') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_property', ['property4_property_id'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_skill', ['skill_slot4_skill_id'], ['SkillId'], onupdate='set null', ondelete='set null') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_skill', ['skill_slot3_skill_id'], ['SkillId'], onupdate='set null', ondelete='set null') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_property', ['property3_property_id'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_equipment_data', ['sub_equipment'], ['id'], onupdate='set null', ondelete='set null') + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_skill', ['skill_slot5_skill_id'], ['SkillId'], onupdate='set null', ondelete='set null') + op.drop_index('sao_static_hero_list_uk', table_name='sao_static_hero_list') + op.drop_column('sao_static_hero_list', 'name') + op.drop_column('sao_static_hero_list', 'skillTableSubId') + op.drop_column('sao_static_hero_list', 'awakeningExp') + op.drop_column('sao_static_hero_list', 'flavorText') + op.drop_column('sao_static_hero_list', 'heroLogId') + op.drop_column('sao_static_hero_list', 'id') + op.drop_column('sao_static_hero_list', 'rarity') + op.drop_column('sao_static_hero_list', 'version') + op.drop_column('sao_static_hero_list', 'nickname') + op.add_column('sao_static_hero_list', sa.Column('HeroLogId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('CharaId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Name', sa.VARCHAR(length=255), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Nickname', sa.VARCHAR(length=255), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Name_en', sa.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('Nickname_en', sa.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('Rarity', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('WeaponTypeId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('HeroLogRoleId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('CostumeTypeId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('UnitId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('DefaultEquipmentId1', sa.BIGINT(), nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('DefaultEquipmentId2', sa.BIGINT(), nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('SkillTableSubId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('HpMin', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('HpMax', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('StrMin', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('StrMax', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('VitMin', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('VitMax', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('IntMin', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('IntMax', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property1PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property1Value1', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property1Value2', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property2PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property2Value1', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property2Value2', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property3PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property3Value1', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property3Value2', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property4PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property4Value1', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Property4Value2', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('FlavorText', sa.VARCHAR(length=255), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('FlavorText_en', sa.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('SalePrice', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('CompositionExp', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('AwakeningExp', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Slot4UnlockLevel', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('Slot5UnlockLevel', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('CollectionEmptyFrameDisplayFlag', sa.BOOLEAN(), nullable=False)) + op.create_foreign_key(None, 'sao_static_hero_list', 'sao_static_equipment_list', ['DefaultEquipmentId1'], ['EquipmentId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_static_hero_list', 'sao_static_equipment_list', ['DefaultEquipmentId2'], ['EquipmentId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_static_hero_list', 'sao_static_property', ['Property2PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_static_hero_list', 'sao_static_property', ['Property3PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_static_hero_list', 'sao_static_property', ['Property4PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_static_hero_list', 'sao_static_property', ['Property1PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.drop_column('sao_static_hero_list', 'enabled') + op.create_primary_key(None, "sao_static_hero_list", ["HeroLogId"]) + op.create_foreign_key(None, 'sao_hero_log_data', 'sao_static_hero_list', ['hero_log_id'], ['HeroLogId'], onupdate='cascade', ondelete='cascade') + op.drop_column('sao_hero_log_data', 'user_hero_log_id') + op.create_foreign_key(None, 'sao_hero_log_data', 'aime_user', ['user'], ['id'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_hero_party', 'sao_hero_log_data', ['user_hero_log_id_3'], ['id'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_hero_party', 'sao_hero_log_data', ['user_hero_log_id_1'], ['id'], onupdate='cascade', ondelete='cascade') + op.create_foreign_key(None, 'sao_hero_party', 'sao_hero_log_data', ['user_hero_log_id_2'], ['id'], onupdate='cascade', ondelete='cascade') + op.create_table('sao_player_hero_card', + sa.Column('id', sa.BIGINT(), nullable=False), + sa.Column('user', sa.INTEGER(), nullable=False), + sa.Column('user_hero_id', sa.INTEGER(), nullable=False), + sa.Column('holographic_flag', sa.BOOLEAN(), server_default='0', nullable=False), + sa.Column('serial', sa.VARCHAR(length=20), nullable=True), + sa.ForeignKeyConstraint(['user'], ['aime_user.id'], onupdate='cascade', ondelete='cascade'), + sa.ForeignKeyConstraint(['user_hero_id'], ['sao_hero_log_data.id'], onupdate='cascade', ondelete='cascade'), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('serial'), + mysql_charset='utf8mb4' + ) + op.alter_column('sao_item_data', 'get_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('now()'), + existing_nullable=False) + op.alter_column('sao_play_sessions', 'play_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('now()'), + existing_nullable=False) + op.add_column('sao_player_quest', sa.Column('quest_type', sa.INTEGER(), server_default='1', nullable=False)) + op.add_column('sao_player_quest', sa.Column('quest_scene_id', sa.BIGINT(), nullable=False)) + op.alter_column('sao_player_quest', 'play_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('now()'), + existing_nullable=False) + op.drop_constraint('sao_player_quest_ibfk_1', 'sao_player_quest', type_='foreignkey') + op.drop_constraint('sao_player_quest_uk', 'sao_player_quest', type_='unique') + op.create_unique_constraint('sao_player_quest_uk', 'sao_player_quest', ['user', 'quest_scene_id']) + op.create_foreign_key(None, "sao_player_quest", "aime_user", ['user'], ['id']) + op.create_foreign_key(None, 'sao_player_quest', 'sao_static_quest', ['quest_scene_id'], ['QuestSceneId'], onupdate='cascade', ondelete='cascade') + op.drop_column('sao_player_quest', 'episode_id') + op.add_column('sao_profile', sa.Column('my_shop', sa.INTEGER(), nullable=True)) + op.add_column('sao_profile', sa.Column('fav_hero', sa.INTEGER(), nullable=True)) + op.add_column('sao_profile', sa.Column('when_register', sa.TIMESTAMP(), server_default=sa.text('now()'), nullable=True)) + op.add_column('sao_profile', sa.Column('last_login_date', sa.TIMESTAMP(), nullable=True)) + op.add_column('sao_profile', sa.Column('last_yui_medal_date', sa.TIMESTAMP(), nullable=True)) + op.add_column('sao_profile', sa.Column('last_bonus_yui_medal_date', sa.TIMESTAMP(), nullable=True)) + op.add_column('sao_profile', sa.Column('last_comeback_date', sa.TIMESTAMP(), nullable=True)) + op.add_column('sao_profile', sa.Column('last_login_bonus_date', sa.TIMESTAMP(), nullable=True)) + op.add_column('sao_profile', sa.Column('ad_confirm_date', sa.TIMESTAMP(), nullable=True)) + op.add_column('sao_profile', sa.Column('login_ct', sa.INTEGER(), server_default='0', nullable=True)) + op.alter_column('sao_profile', 'own_vp', + existing_type=mysql.INTEGER(display_width=11), + server_default='0', + existing_nullable=True) + op.create_foreign_key(None, 'sao_profile', 'sao_hero_log_data', ['fav_hero'], ['id'], onupdate='cascade', ondelete='set null') + op.drop_column('sao_static_item_list', 'name') + op.drop_column('sao_static_item_list', 'itemId') + op.drop_column('sao_static_item_list', 'itemTypeId') + op.drop_column('sao_static_item_list', 'flavorText') + op.drop_column('sao_static_item_list', 'id') + op.drop_column('sao_static_item_list', 'rarity') + op.drop_column('sao_static_item_list', 'enabled') + op.add_column('sao_static_item_list', sa.Column('ItemId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('ItemTypeId', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('Name', sa.VARCHAR(length=255), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('Name_en', sa.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_item_list', sa.Column('Rarity', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('Value', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('PropertyId', sa.BIGINT(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('PropertyValue1Min', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('PropertyValue1Max', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('PropertyValue2Min', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('PropertyValue2Max', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('FlavorText', sa.VARCHAR(length=255), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('FlavorText_en', sa.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_item_list', sa.Column('SalePrice', sa.INTEGER(), nullable=False)) + op.add_column('sao_static_item_list', sa.Column('ItemIcon', sa.VARCHAR(length=255), nullable=False)) + op.drop_index('sao_static_item_list_uk', table_name='sao_static_item_list') + op.create_foreign_key(None, 'sao_static_item_list', 'sao_static_property', ['PropertyId'], ['PropertyId'], onupdate='cascade', ondelete='cascade') + op.drop_column('sao_static_item_list', 'version') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint("sao_static_item_list_ibfk_1", 'sao_static_item_list', type_='foreignkey') + op.drop_column('sao_static_item_list', 'ItemIcon') + op.drop_column('sao_static_item_list', 'SalePrice') + op.drop_column('sao_static_item_list', 'FlavorText_en') + op.drop_column('sao_static_item_list', 'FlavorText') + op.drop_column('sao_static_item_list', 'PropertyValue2Max') + op.drop_column('sao_static_item_list', 'PropertyValue2Min') + op.drop_column('sao_static_item_list', 'PropertyValue1Max') + op.drop_column('sao_static_item_list', 'PropertyValue1Min') + op.drop_column('sao_static_item_list', 'PropertyId') + op.drop_column('sao_static_item_list', 'Value') + op.drop_column('sao_static_item_list', 'Rarity') + op.drop_column('sao_static_item_list', 'Name') + op.drop_column('sao_static_item_list', 'ItemTypeId') + op.drop_column('sao_static_item_list', 'ItemId') + op.add_column('sao_static_item_list', sa.Column('version', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_item_list', sa.Column('enabled', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True)) + op.add_column('sao_static_item_list', sa.Column('rarity', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_item_list', sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False)) + op.add_column('sao_static_item_list', sa.Column('flavorText', mysql.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_item_list', sa.Column('itemTypeId', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_item_list', sa.Column('itemId', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_item_list', sa.Column('name', mysql.VARCHAR(length=255), nullable=True)) + op.add_column('sao_hero_log_data', sa.Column('user_hero_log_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=False)) + op.drop_column('sao_static_item_list', 'Name_en') + op.create_index('sao_static_item_list_uk', 'sao_static_item_list', ['version', 'itemId'], unique=True) + op.drop_constraint("sao_hero_log_data_ibfk_1", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_2", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_3", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_4", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_5", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_6", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_7", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_8", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_9", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_10", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_11", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_12", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint("sao_hero_log_data_ibfk_13", 'sao_hero_log_data', type_='foreignkey') + op.drop_constraint('sao_hero_log_data_uk', 'sao_hero_log_data', type_='unique') + op.create_foreign_key("sao_hero_log_data_ibfk_1", 'sao_hero_log_data', "aime_user", ['user'], ['id']) + op.drop_column('sao_hero_log_data', 'converted_card_num') + op.drop_column('sao_hero_log_data', 'property4_value2') + op.drop_column('sao_hero_log_data', 'property4_value1') + op.drop_column('sao_hero_log_data', 'property4_property_id') + op.drop_column('sao_hero_log_data', 'property3_value2') + op.drop_column('sao_hero_log_data', 'property3_value1') + op.drop_column('sao_hero_log_data', 'property3_property_id') + op.drop_column('sao_hero_log_data', 'property2_value2') + op.drop_column('sao_hero_log_data', 'property2_value1') + op.drop_column('sao_hero_log_data', 'property2_property_id') + op.drop_column('sao_hero_log_data', 'property1_value2') + op.drop_column('sao_hero_log_data', 'property1_value1') + op.drop_column('sao_hero_log_data', 'property1_property_id') + op.drop_column('sao_hero_log_data', 'is_protect') + op.drop_column('sao_hero_log_data', 'is_shop_purchase') + op.drop_column('sao_hero_log_data', 'awakening_exp') + op.drop_column('sao_hero_log_data', 'awakening_stage') + op.drop_column('sao_hero_log_data', 'is_awakenable') + op.drop_column('sao_hero_log_data', 'max_level_extend_num') + op.drop_column('sao_hero_log_data', 'hero_log_id') + op.alter_column('sao_hero_log_data', 'get_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('CURRENT_TIMESTAMP'), + existing_nullable=False) + op.alter_column('sao_hero_log_data', 'skill_slot5_skill_id', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + op.alter_column('sao_hero_log_data', 'skill_slot4_skill_id', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + op.alter_column('sao_hero_log_data', 'skill_slot3_skill_id', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + op.alter_column('sao_hero_log_data', 'skill_slot2_skill_id', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + op.alter_column('sao_hero_log_data', 'skill_slot1_skill_id', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + op.alter_column('sao_hero_log_data', 'sub_equipment', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + op.alter_column('sao_hero_log_data', 'main_weapon', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + op.create_unique_constraint('sao_hero_log_data_uk', 'sao_hero_log_data', ['user', 'user_hero_log_id']) + op.drop_constraint("sao_static_hero_list_ibfk_1", 'sao_static_hero_list', type_='foreignkey') + op.drop_constraint("sao_static_hero_list_ibfk_2", 'sao_static_hero_list', type_='foreignkey') + op.drop_constraint("sao_static_hero_list_ibfk_3", 'sao_static_hero_list', type_='foreignkey') + op.drop_constraint("sao_static_hero_list_ibfk_4", 'sao_static_hero_list', type_='foreignkey') + op.drop_constraint("sao_static_hero_list_ibfk_5", 'sao_static_hero_list', type_='foreignkey') + op.drop_constraint("sao_static_hero_list_ibfk_6", 'sao_static_hero_list', type_='foreignkey') + op.drop_column('sao_static_hero_list', 'CollectionEmptyFrameDisplayFlag') + op.drop_column('sao_static_hero_list', 'Slot5UnlockLevel') + op.drop_column('sao_static_hero_list', 'Slot4UnlockLevel') + op.drop_column('sao_static_hero_list', 'AwakeningExp') + op.drop_column('sao_static_hero_list', 'CompositionExp') + op.drop_column('sao_static_hero_list', 'SalePrice') + op.drop_column('sao_static_hero_list', 'FlavorText_en') + op.drop_column('sao_static_hero_list', 'FlavorText') + op.drop_column('sao_static_hero_list', 'Property4Value2') + op.drop_column('sao_static_hero_list', 'Property4Value1') + op.drop_column('sao_static_hero_list', 'Property4PropertyId') + op.drop_column('sao_static_hero_list', 'Property3Value2') + op.drop_column('sao_static_hero_list', 'Property3Value1') + op.drop_column('sao_static_hero_list', 'Property3PropertyId') + op.drop_column('sao_static_hero_list', 'Property2Value2') + op.drop_column('sao_static_hero_list', 'Property2Value1') + op.drop_column('sao_static_hero_list', 'Property2PropertyId') + op.drop_column('sao_static_hero_list', 'Property1Value2') + op.drop_column('sao_static_hero_list', 'Property1Value1') + op.drop_column('sao_static_hero_list', 'Property1PropertyId') + op.drop_column('sao_static_hero_list', 'IntMax') + op.drop_column('sao_static_hero_list', 'IntMin') + op.drop_column('sao_static_hero_list', 'VitMax') + op.drop_column('sao_static_hero_list', 'VitMin') + op.drop_column('sao_static_hero_list', 'StrMax') + op.drop_column('sao_static_hero_list', 'StrMin') + op.drop_column('sao_static_hero_list', 'HpMax') + op.drop_column('sao_static_hero_list', 'SkillTableSubId') + op.drop_column('sao_static_hero_list', 'DefaultEquipmentId2') + op.drop_column('sao_static_hero_list', 'DefaultEquipmentId1') + op.drop_column('sao_static_hero_list', 'UnitId') + op.drop_column('sao_static_hero_list', 'CostumeTypeId') + op.drop_column('sao_static_hero_list', 'HeroLogRoleId') + op.drop_column('sao_static_hero_list', 'WeaponTypeId') + op.drop_column('sao_static_hero_list', 'Rarity') + op.drop_column('sao_static_hero_list', 'Nickname_en') + op.drop_column('sao_static_hero_list', 'Name_en') + op.drop_column('sao_static_hero_list', 'Nickname') + op.drop_column('sao_static_hero_list', 'Name') + op.drop_column('sao_static_hero_list', 'CharaId') + op.drop_column('sao_static_hero_list', 'HeroLogId') + op.add_column('sao_static_hero_list', sa.Column('nickname', mysql.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('version', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('enabled', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('rarity', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False)) + op.add_column('sao_static_hero_list', sa.Column('heroLogId', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('flavorText', mysql.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('awakeningExp', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('skillTableSubId', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_hero_list', sa.Column('name', mysql.VARCHAR(length=255), nullable=True)) + op.drop_column('sao_static_hero_list', 'HpMin') + op.create_index('sao_static_hero_list_uk', 'sao_static_hero_list', ['version', 'heroLogId'], unique=True) + op.drop_constraint("sao_equipment_data_ibfk_2", 'sao_equipment_data', type_='foreignkey') + op.drop_constraint("sao_equipment_data_ibfk_3", 'sao_equipment_data', type_='foreignkey') + op.drop_constraint("sao_equipment_data_ibfk_4", 'sao_equipment_data', type_='foreignkey') + op.drop_constraint("sao_equipment_data_ibfk_5", 'sao_equipment_data', type_='foreignkey') + op.drop_constraint("sao_equipment_data_ibfk_6", 'sao_equipment_data', type_='foreignkey') + op.alter_column('sao_equipment_data', 'get_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('CURRENT_TIMESTAMP'), + existing_nullable=False) + op.drop_column('sao_equipment_data', 'converted_card_num') + op.drop_column('sao_equipment_data', 'property4_value2') + op.drop_column('sao_equipment_data', 'property4_value1') + op.drop_column('sao_equipment_data', 'property4_property_id') + op.drop_column('sao_equipment_data', 'property3_value2') + op.drop_column('sao_equipment_data', 'property3_value1') + op.drop_column('sao_equipment_data', 'property3_property_id') + op.drop_column('sao_equipment_data', 'property2_value2') + op.drop_column('sao_equipment_data', 'property2_value1') + op.drop_column('sao_equipment_data', 'property2_property_id') + op.drop_column('sao_equipment_data', 'property1_value2') + op.drop_column('sao_equipment_data', 'property1_value1') + op.drop_column('sao_equipment_data', 'property1_property_id') + op.drop_column('sao_equipment_data', 'is_protect') + op.drop_column('sao_equipment_data', 'is_shop_purchase') + op.drop_constraint("sao_static_equipment_list_ibfk_1", 'sao_static_equipment_list', type_='foreignkey') + op.drop_constraint("sao_static_equipment_list_ibfk_2", 'sao_static_equipment_list', type_='foreignkey') + op.drop_constraint("sao_static_equipment_list_ibfk_3", 'sao_static_equipment_list', type_='foreignkey') + op.drop_constraint("sao_static_equipment_list_ibfk_4", 'sao_static_equipment_list', type_='foreignkey') + op.drop_column('sao_static_equipment_list', 'FlavorText_en') + op.drop_column('sao_static_equipment_list', 'FlavorText') + op.drop_column('sao_static_equipment_list', 'AwakeningExp') + op.drop_column('sao_static_equipment_list', 'CompositionExp') + op.drop_column('sao_static_equipment_list', 'SalePrice') + op.drop_column('sao_static_equipment_list', 'Property4Value2') + op.drop_column('sao_static_equipment_list', 'Property4Value1') + op.drop_column('sao_static_equipment_list', 'Property4PropertyId') + op.drop_column('sao_static_equipment_list', 'Property3Value2') + op.drop_column('sao_static_equipment_list', 'Property3Value1') + op.drop_column('sao_static_equipment_list', 'Property3PropertyId') + op.drop_column('sao_static_equipment_list', 'Property2Value2') + op.drop_column('sao_static_equipment_list', 'Property2Value1') + op.drop_column('sao_static_equipment_list', 'Property2PropertyId') + op.drop_column('sao_static_equipment_list', 'Property1Value2') + op.drop_column('sao_static_equipment_list', 'Property1Value1') + op.drop_column('sao_static_equipment_list', 'Property1PropertyId') + op.drop_column('sao_static_equipment_list', 'SkillCondition') + op.drop_column('sao_static_equipment_list', 'StrengthIncrement') + op.drop_column('sao_static_equipment_list', 'Power') + op.drop_column('sao_static_equipment_list', 'Rarity') + op.drop_column('sao_static_equipment_list', 'Name') + op.drop_column('sao_static_equipment_list', 'WeaponTypeId') + op.drop_column('sao_static_equipment_list', 'EquipmentType') + op.drop_column('sao_static_equipment_list', 'EquipmentId') + op.add_column('sao_static_equipment_list', sa.Column('equipmentType', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_equipment_list', sa.Column('version', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_equipment_list', sa.Column('weaponTypeId', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_equipment_list', sa.Column('enabled', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True)) + op.add_column('sao_static_equipment_list', sa.Column('rarity', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_equipment_list', sa.Column('equipmentId', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_equipment_list', sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False)) + op.add_column('sao_static_equipment_list', sa.Column('flavorText', mysql.VARCHAR(length=255), nullable=True)) + op.add_column('sao_static_equipment_list', sa.Column('name', mysql.VARCHAR(length=255), nullable=True)) + op.drop_column('sao_static_equipment_list', 'Name_en') + op.create_index('sao_static_equipment_list_uk', 'sao_static_equipment_list', ['version', 'equipmentId'], unique=True) + op.drop_constraint("sao_profile_ibfk_2", 'sao_profile', type_='foreignkey') + op.alter_column('sao_profile', 'own_vp', + existing_type=mysql.INTEGER(display_width=11), + server_default=sa.text("'300'"), + existing_nullable=True) + op.drop_column('sao_profile', 'login_ct') + op.drop_column('sao_profile', 'ad_confirm_date') + op.drop_column('sao_profile', 'last_login_bonus_date') + op.drop_column('sao_profile', 'last_comeback_date') + op.drop_column('sao_profile', 'last_bonus_yui_medal_date') + op.drop_column('sao_profile', 'last_yui_medal_date') + op.drop_column('sao_profile', 'last_login_date') + op.drop_column('sao_profile', 'when_register') + op.drop_column('sao_profile', 'fav_hero') + op.drop_column('sao_profile', 'my_shop') + op.add_column('sao_player_quest', sa.Column('episode_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=False)) + op.drop_constraint("sao_player_quest_ibfk_2", 'sao_player_quest', type_='foreignkey') + op.drop_constraint("sao_player_quest_ibfk_1", 'sao_player_quest', type_='foreignkey') + op.drop_constraint('sao_player_quest_uk', 'sao_player_quest', type_='unique') + op.create_foreign_key("sao_player_quest_ibfk_1", "sao_player_quest", "aime_user", ['user'], ['id']) + op.create_unique_constraint('sao_player_quest_uk', 'sao_player_quest', ['user', 'episode_id']) + op.alter_column('sao_player_quest', 'play_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('CURRENT_TIMESTAMP'), + existing_nullable=False) + op.drop_column('sao_player_quest', 'quest_scene_id') + op.drop_column('sao_player_quest', 'quest_type') + op.alter_column('sao_play_sessions', 'play_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('CURRENT_TIMESTAMP'), + existing_nullable=False) + op.alter_column('sao_item_data', 'get_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('CURRENT_TIMESTAMP'), + existing_nullable=False) + op.drop_constraint("sao_hero_party_ibfk_2", 'sao_hero_party', type_='foreignkey') + op.drop_constraint("sao_hero_party_ibfk_3", 'sao_hero_party', type_='foreignkey') + op.drop_constraint("sao_hero_party_ibfk_4", 'sao_hero_party', type_='foreignkey') + op.alter_column('sao_end_sessions', 'play_date', + existing_type=mysql.TIMESTAMP(), + server_default=sa.text('CURRENT_TIMESTAMP'), + existing_nullable=False) + op.drop_table('sao_player_hero_card') + op.drop_table('sao_player_ex_bonus') + op.drop_table('sao_static_tower') + op.drop_table('sao_static_skill_table') + op.drop_table('sao_static_side_quest') + op.drop_table('sao_static_ex_tower') + op.drop_table('sao_static_ex_bonus') + op.drop_table('sao_static_episode') + op.drop_table('sao_player_tutorial') + op.drop_table('sao_player_resource_card') + op.add_column('sao_static_quest', sa.Column('version', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.drop_column('sao_static_quest', 'TrioRewardVp') + op.drop_column('sao_static_quest', 'PairRewardVp') + op.drop_column('sao_static_quest', 'SingleRewardVp') + op.drop_column('sao_static_quest', 'TrioExpRate') + op.drop_column('sao_static_quest', 'PairExpRate') + op.drop_column('sao_static_quest', 'FailedPlayerExp') + op.drop_column('sao_static_quest', 'SuccessPlayerExp') + op.drop_column('sao_static_quest', 'PlayerTraceTableSubId') + op.drop_column('sao_static_quest', 'RewardTableSubId') + op.drop_column('sao_static_quest', 'LimitResurrection') + op.drop_column('sao_static_quest', 'LimitDefault') + op.drop_column('sao_static_quest', 'ColRate') + op.drop_column('sao_static_quest', 'Tutorial') + op.drop_column('sao_static_quest', 'SortNo') + op.drop_column('sao_static_quest', 'QuestSceneId') + op.add_column('sao_static_quest', sa.Column('enabled', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True)) + op.add_column('sao_static_quest', sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False)) + op.add_column('sao_static_quest', sa.Column('questSceneId', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_quest', sa.Column('sortNo', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) + op.add_column('sao_static_quest', sa.Column('name', mysql.VARCHAR(length=255), nullable=True)) + op.create_index('sao_static_quest_uk', 'sao_static_quest', ['version', 'questSceneId'], unique=True) + # ### end Alembic commands ### diff --git a/core/data/schema/card.py b/core/data/schema/card.py index 798dd03..6205b4c 100644 --- a/core/data/schema/card.py +++ b/core/data/schema/card.py @@ -25,6 +25,9 @@ aime_card = Table( class CardData(BaseData): + moble_os_codes = set([0x06, 0x07, 0x10, 0x12, 0x13, 0x14, 0x15, 0x17, 0x18]) + card_os_codes = set([0x20, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7]) + async def get_card_by_access_code(self, access_code: str) -> Optional[Row]: sql = aime_card.select(aime_card.c.access_code == access_code) @@ -140,6 +143,11 @@ class CardData(BaseData): if not result: self.logger.error(f"Failed to update IDm to {idm} for {access_code}") + async def set_access_code_by_access_code(self, old_ac: str, new_ac: str) -> None: + result = await self.execute(aime_card.update(aime_card.c.access_code == old_ac).values(access_code=new_ac)) + if not result: + self.logger.error(f"Failed to change card access code from {old_ac} to {new_ac}") + def to_access_code(self, luid: str) -> str: """ Given a felica cards internal 16 hex character luid, convert it to a 0-padded 20 digit access code as a string diff --git a/example_config/sao.yaml b/example_config/sao.yaml index 0209ffe..d405461 100644 --- a/example_config/sao.yaml +++ b/example_config/sao.yaml @@ -2,12 +2,19 @@ server: enable: True loglevel: "info" auto_register: True + photon_app_id: "7df3a2f6-d69d-4073-aafe-810ee61e1cea" + data_version: 1 + game_version: 33 crypt: enable: False key: "" - iv: "" hash: verify_hash: False - hash_base: "" \ No newline at end of file + hash_base: "" + +card: + enable: True + crypt_password: "" + crypt_salt: "" diff --git a/titles/sao/__init__.py b/titles/sao/__init__.py index b6943e6..501af9b 100644 --- a/titles/sao/__init__.py +++ b/titles/sao/__init__.py @@ -1,9 +1,11 @@ from .index import SaoServlet from .const import SaoConstants from .database import SaoData +from .frontend import SaoFrontend from .read import SaoReader index = SaoServlet database = SaoData +frontend = SaoFrontend reader = SaoReader game_codes = [SaoConstants.GAME_CODE] diff --git a/titles/sao/base.py b/titles/sao/base.py index 8d5650d..a589778 100644 --- a/titles/sao/base.py +++ b/titles/sao/base.py @@ -1,448 +1,1173 @@ import logging from csv import * -from random import choice +from random import choice, randint from typing import Dict, List from os import path +import re +from sqlalchemy.engine import Row -from core.data import Data from core import CoreConfig from .config import SaoConfig from .database import SaoData +from .const import GameconnectCmd, RewardType, ExBonusCondition, QuestType from titles.sao.handlers.base import * +import csv class SaoBase: + DATA_LIST = {} def __init__(self, core_cfg: CoreConfig, game_cfg: SaoConfig) -> None: self.core_cfg = core_cfg self.game_cfg = game_cfg self.data = SaoData(core_cfg) - self.version = 0 self.logger = logging.getLogger("sao") - def load_data_csv(self, file: str) -> List[Dict]: - ret = [] - if path.exists(f"titles/sao/data/{file}.csv"): - with open(f"titles/sao/data/{file}.csv", "r", encoding="utf8") as f: - data = csv.DictReader(f, delimiter=',') - for x in data: - ret.append(x) - + def load_data_csv(self, file: str, version: int = 1, base_ver: int = 1) -> List[Dict]: + if base_ver > version: + self.logger.warning(f"load_data_csv: Cannot use base version higher then requested version ({base_ver} > {version})") + return [] + + for x in range(version, base_ver - 1, -1): + ret = self.DATA_LIST.get(x, {}).get(file, []) + if ret: + break + + if not ret and base_ver != 1: + ret = self.DATA_LIST.get(1, {}).get(file, []) + + if ret: return ret - self.logger.warning(f"Failed to find csv file {file}.csv") + found = False + for x in range(version, base_ver - 1, -1): + fname = f"./titles/sao/data/{x}/{file}.csv" + if path.exists(fname): + found_ver = x + found = True + break + + if not found and base_ver != 1: # v1 will always be fallback if it isn't already + fname = f"./titles/sao/data/1/{file}.csv" + if path.exists(fname): + found_ver = 1 + found = True + + if not found: + self.logger.warning(f"load_data_csv: Failed to find v{version} csv file {fname}") + return [] + + ret = [] + with open(fname, "r", encoding="utf8") as f: + data = csv.DictReader(f, delimiter=',') + for x in data: + newdict = {} + for k, v in x.items(): + newkey = k + if k.startswith("// "): + newkey = k.replace("// ", "") + + if v.isdigit(): + newdict[newkey] = int(v) + elif v.lower() == "true": + newdict[newkey] = True + elif v.lower() == "false": + newdict[newkey] = False + elif re.match(r"^\d\d\d\d\/\d\d\/\d\d \d{1,2}:\d\d:\d\d$", v): + newdict[newkey] = datetime.strptime(v, "%Y/%m/%d %H:%M:%S") + elif re.match(r"^\d\d\d\d\/\d\d\/\d\d$", v): + newdict[newkey] = datetime.strptime(v, "%Y/%m/%d") + else: + newdict[newkey] = v + ret.append(newdict) + + # Cache the CSV data in memory + if found_ver not in self.DATA_LIST: + self.DATA_LIST[found_ver] = {} + self.DATA_LIST[found_ver][file] = ret + return ret - async def handle_noop(self, header: SaoRequestHeader, request: bytes) -> bytes: - self.logger.info(f"Using Generic handler") - resp_thing = SaoNoopResponse(header.cmd + 1) - return resp_thing.make() + async def add_reward(self, reward: Dict, user_id: int): + reward_type = int(reward.get("CommonRewardType", "0")) + if reward_type == RewardType.HeroLog: + reward_hero_data = await self.data.static.get_hero_by_id(reward['CommonRewardId']) + now_have_skills = await self.hero_default_skills(reward_hero_data['SkillTableSubId']) + + new_hero_id = await self.data.item.put_hero_log( + user_id, + reward['CommonRewardId'], + 1, + 0, + None, + None, + now_have_skills[0], + now_have_skills[1], + now_have_skills[2], + now_have_skills[3], + now_have_skills[4], + ) + self.logger.info(f"Rewarded user {user_id} with hero {reward['CommonRewardId']} (ID {new_hero_id})") + # TODO: add properties + + elif reward_type == RewardType.Equipment: + new_equip_id = await self.data.item.put_equipment(user_id, reward['CommonRewardId']) + self.logger.info(f"Rewarded user {user_id} with equipment {reward['CommonRewardId']} (ID {new_equip_id})") + + elif reward_type == RewardType.Item: + new_item_id = await self.data.item.put_item(user_id, reward['CommonRewardId']) + self.logger.info(f"Rewarded user {user_id} with item {reward['CommonRewardId']} (ID {new_item_id})") + + elif reward_type == RewardType.Col: + col_num = int(reward['CommonRewardNum']) + self.logger.info(f"Rewarded user {user_id} with {col_num} Col") + await self.data.profile.add_col(user_id, col_num) - async def handle_c122(self, header: SaoRequestHeader, request: bytes) -> bytes: - #common/get_maintenance_info - resp = SaoGetMaintResponse(header.cmd +1) - return resp.make() + elif reward_type == RewardType.VP: + vp_num = int(reward['CommonRewardNum']) + self.logger.info(f"Rewarded user {user_id} with {vp_num} VP") + await self.data.profile.add_vp(user_id, vp_num) - async def handle_c12a(self, header: SaoRequestHeader, request: bytes) -> bytes: - #common/give_free_ticket - req = SaoGiveFreeTicketRequest(header, request) - self.logger.info(f"Give {req.give_num} free tickets (id {req.ticket_id}) to user {req.user_id}") - resp = SaoGiveFreeTicketResponse(header.cmd +1) - return resp.make() + elif reward_type == RewardType.YuiMadal: + medal_num = int(reward['CommonRewardNum']) + self.logger.info(f"Rewarded user {user_id} with {medal_num} Yui Medals") + await self.data.profile.add_yui_medals(user_id, medal_num) + + else: + self.logger.warn(f"User {user_id} Unhandled reward type {reward_type} -> {reward}") - async def handle_c12e(self, header: SaoRequestHeader, request: bytes) -> bytes: - #common/ac_cabinet_boot_notification - resp = SaoCommonAcCabinetBootNotificationResponse(header.cmd +1) - return resp.make() + async def hero_default_skills(self, skill_table_id: int) -> List[int]: + skills = await self.data.static.get_skill_table_by_subid(skill_table_id) + if not skills: + self.logger.error(f"Failed to find skill table {skill_table_id}! Please run the reader") + return [None, None, None, None, None] - async def handle_c100(self, header: SaoRequestHeader, request: bytes) -> bytes: + default_skills = [] + now_have_skills = [None, None, None, None, None] + for skill in skills: + if skill['LevelObtained'] == 1 and skill['AwakeningId'] == 0: + default_skills.append(skill['SkillId']) + + for skill in default_skills: + skill_info = await self.data.static.get_skill_by_id(skill) + skill_slot = skill_info['Level'] - 1 + if now_have_skills[skill_slot] is not None: + now_have_skills[skill] + + return now_have_skills + + async def handle_noop(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + return SaoNoopResponse(header.cmd + 1).make() + + async def handle_c000(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #ticket/ticket + req = SaoTicketRequest(header, request) + return SaoTicketResponse().make() + + async def handle_c100(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #common/get_app_versions - resp = SaoCommonGetAppVersionsRequest(header.cmd +1) + resp = SaoGetAppVersionsResponse() + resp.data_list.append(AppVersionData.from_args(self.game_cfg.server.game_version, datetime.fromtimestamp(0))) return resp.make() - async def handle_c102(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c102(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #common/master_data_version_check - resp = SaoMasterDataVersionCheckResponse(header.cmd +1) - return resp.make() + req = SaoMasterDataVersionCheckRequest(header, request) + self.logger.info(f"Cab at {src_ip} checked in with master data v{req.current_data_version}") + return SaoMasterDataVersionCheckResponse(self.game_cfg.server.data_version, req.current_data_version).make() - async def handle_c10a(self, header: SaoRequestHeader, request: bytes) -> bytes: - #common/paying_play_start - resp = SaoCommonPayingPlayStartRequest(header.cmd +1) - return resp.make() + async def handle_c104(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #common/login + req = SaoLoginRequest(header, request) - async def handle_ca02(self, header: SaoRequestHeader, request: bytes) -> bytes: - #quest_multi_play_room/get_quest_scene_multi_play_photon_server - resp = SaoGetQuestSceneMultiPlayPhotonServerResponse(header.cmd +1) - return resp.make() - - async def handle_c11e(self, header: SaoRequestHeader, request: bytes) -> bytes: - #common/get_auth_card_data - req = SaoGetAuthCardDataRequest(header, request) - - #Check authentication user_id = await self.data.card.get_user_id_from_card( req.access_code ) - if not user_id: - user_id = await self.data.user.create_user() #works - card_id = await self.data.card.create_card(user_id, req.access_code) - if req.access_code.startswith("5"): - await self.data.card.set_idm_by_access_code(card_id, req.chip_id[:16]) - elif (req.access_code.startswith("010") or req.access_code.startswith("3")) and int(req.chip_id[:8], 16) != 0x04030201: - await self.data.card.set_chip_id_by_access_code(card_id, int(req.chip_id[:8], 16)) + card = await self.data.card.get_card_by_idm(req.chip_id[:16]) + if card: + user_id = card['user'] + card_id = card['id'] + await self.data.card.set_access_code_by_access_code(card['access_code'], req.access_code) + + else: + user_id = await self.data.user.create_user() #works + card_id = await self.data.card.create_card(user_id, req.access_code) if card_id is None: user_id = -1 self.logger.error("Failed to register card!") - - # Create profile with 3 basic heroes - profile_id = await self.data.profile.create_profile(user_id) - await self.data.item.put_hero_log(user_id, 101000010, 1, 0, 201000000, 0, 1002, 1003, 1014, 30001, 30310) - await self.data.item.put_hero_log(user_id, 102000010, 1, 0, 202000000, 0, 3001, 3002, 3004, 30007, 3011) - await self.data.item.put_hero_log(user_id, 105000010, 1, 0, 209000000, 0, 10005, 10002, 10004, 30006, 10003) - await self.data.item.put_hero_log(user_id, 101000110, 1, 0, 201000000, 101000110, 2002, 2001, 2014, 0, 0) - await self.data.item.put_hero_party(user_id, 0, 101000010, 102000010, 105000010) - await self.data.item.put_equipment_data(user_id, 201000000, 1, 200, 0, 0, 0) - await self.data.item.put_equipment_data(user_id, 202000000, 1, 200, 0, 0, 0) - await self.data.item.put_equipment_data(user_id, 209000000, 1, 200, 0, 0, 0) - await self.data.item.put_equipment_data(user_id, 101000110, 1, 200, 0, 0, 0) - await self.data.item.put_player_quest(user_id, 1001, True, 300, 0, 0, 1) - - # Force the tutorial stage to be completed due to potential crash in-game + self.logger.info(f"Registered card {req.access_code} to user {user_id} from {req.serial_no}") + + if req.access_code.startswith("5"): + await self.data.card.set_idm_by_access_code(req.access_code, req.chip_id[:16]) + elif (req.access_code.startswith("010") or req.access_code.startswith("3")) and int(req.chip_id[:8], 16) != 0x04030201: + await self.data.card.set_chip_id_by_access_code(req.access_code, int(req.chip_id[:8], 16)) + + profile_data = await self.data.profile.get_profile(user_id) - self.logger.info(f"User Authenticated: { req.access_code } | { user_id }") + if not profile_data: + profile_id = await self.data.profile.create_profile(user_id) + if profile_id: + equip1 = await self.data.item.put_equipment(user_id, 101000000) + equip2 = await self.data.item.put_equipment(user_id, 102000000) + equip3 = await self.data.item.put_equipment(user_id, 109000000) + if not equip1 or not equip2 or not equip3: + self.logger.error(f"Failed to create profile for user {user_id} from {req.serial_no} (could not add equipment)") + return SaoNoopResponse(GameconnectCmd.LOGIN_RESPONSE).make() + + hero1 = await self.data.item.put_hero_log(user_id, 101000010, 1, 0, equip1, None, 1002, 1003, 1014, None, None) + hero2 = await self.data.item.put_hero_log(user_id, 102000010, 1, 0, equip2, None, 3001, 3002, 3004, None, None) + hero3 = await self.data.item.put_hero_log(user_id, 105000010, 1, 0, equip3, None, 10005, 10002, 10004, None, None) + if not hero1 or not hero2 or not hero3: + self.logger.error(f"Failed to create profile for user {user_id} from {req.serial_no} (could not add heros)") + return SaoNoopResponse(GameconnectCmd.LOGIN_RESPONSE).make() + + await self.data.item.put_hero_party(user_id, 0, hero1, hero2, hero3) + self.logger.info(f"Create profile {profile_id} for user {user_id} from {req.serial_no}") + else: + self.logger.error(f"Failed to create profile for user {user_id} from {req.serial_no}") + return SaoNoopResponse(GameconnectCmd.LOGIN_RESPONSE).make() + resp = SaoLoginResponse(user_id, True, False) + + else: + is_login_today = False + + if profile_data['last_login_date']: + last_login_time = int(profile_data["last_login_date"].timestamp()) + midnight_today_ts = int( + datetime.now() + .replace(hour=0, minute=0, second=0, microsecond=0) + .timestamp() + ) + + if last_login_time > midnight_today_ts: + is_login_today = True + + if not is_login_today: + await self.data.profile.add_vp(user_id, 100) + + resp = SaoLoginResponse(user_id, profile_data['login_ct'] < 1, is_login_today) + + await self.data.profile.user_login(user_id) + return resp.make() + + async def handle_c106(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/logout + req = SaoLogoutRequest(header, request) + self.logger.info(f"User {req.user_id} Logout from {'game' if req.cabinet_type == 0 else 'terminal'} @ {src_ip} with {req.remaining_ticket_num} tickets remaining") + return SaoNoopResponse(GameconnectCmd.LOGOUT_RESPONSE).make() + + async def handle_c108(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/logout_ticket_unpurchased + req = SaoLogoutTicketUnpurchasedRequest(header, request) + self.logger.info(f"User {req.user_id} Logout from {'game' if req.cabinet_type == 0 else 'terminal'} @ {src_ip} without buying a ticket") + return SaoNoopResponse(GameconnectCmd.LOGOUT_TICKET_UNPURCHASED_RESPONSE).make() + + async def handle_c10a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/paying_play_start + req = SaoPayingPlayStartRequest(header, request) + self.logger.info(f"User {req.paying_user_id} started paying session @ {req.store_name} ({src_ip}) on cab {req.serial_no}") + resp = SaoPayingPlayStartResponse() + # TODO: session management + return resp.make() + + async def handle_c10c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/paying_play_end + req = SaoPayingPlayEndRequest(header, request) + self.logger.info(f"User {req.paying_user_id} ended paying session {req.paying_session_id} @ {req.store_name} ({src_ip}) on cab {req.serial_no} after {req.played_amount} {req.played_type} type games") + return SaoNoopResponse(GameconnectCmd.PAYING_PLAY_END_RESPONSE).make() + + async def handle_c10e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/purchase_ticket + req = SaoPurchaseTicketRequest(header, request) + self.logger.info(f"User {req.user_id} pruchased {req.purchase_num} tickets (ID {req.ticket_id}) @ {src_ip} with discout type {req.discount_type}") + return SaoNoopResponse(GameconnectCmd.PURCHASE_TICKET_RESPONSE).make() + + async def handle_c110(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/consume_ticket + req = SaoConsumeTicketRequest(header, request) + self.logger.info(f"User {req.user_id} consumed {req.consume_num} tickets (ID {req.ticket_id}) @ {src_ip} with discout type {req.discount_type} on {req.act_type}") + return SaoNoopResponse(GameconnectCmd.CONSUME_TICKET_RESPONSE).make() + + async def handle_c112(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/add_credit + req = SaoAddCreditRequest(header, request) + self.logger.info(f"User {req.user_id} added {req.add_num} credits to a {'game' if req.cabinet_type == 0 else 'terminal'} @ {src_ip}") + return SaoNoopResponse(GameconnectCmd.ADD_CREDIT_RESPONSE).make() + + async def handle_c114(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/consume_credit + req = SaoConsumeCreditRequest(header, request) + self.logger.info(f"User {req.user_id} consumed {req.consume_num} credits on a {'game' if req.cabinet_type == 0 else 'terminal'} @ {req.store_id} ({src_ip}) on {req.act_type}") + return SaoNoopResponse(GameconnectCmd.CONSUME_CREDIT_RESPONSE).make() + + async def handle_c116(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/purchase_ticket_guest + req = SaoPurchaseTicketGuestRequest(header, request) + self.logger.info(f"Guest purchased {req.purchase_num} tickets on a {'game' if req.cabinet_type == 0 else 'terminal'} @ {req.store_id} ({src_ip} | SN {req.serial_no})") + return SaoNoopResponse(GameconnectCmd.PURCHASE_TICKET_GUEST_RESPONSE).make() + + async def handle_c118(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/consume_ticket_guest + req = SaoConsumeTicketGuestRequest(header, request) + self.logger.info(f"Guest consumed {req.consume_num} tickets @ {req.store_id} ({src_ip} | SN {req.serial_no}) with discout type {req.discount_type} on {req.act_type}") + return SaoNoopResponse(GameconnectCmd.CONSUME_TICKET_GUEST_RESPONSE).make() + + async def handle_c11a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/add_credit_guest + req = SaoAddCreditGuestRequest(header, request) + self.logger.info(f"Guest added {req.add_num} credits to a {'game' if req.cabinet_type == 0 else 'terminal'} @ {req.store_id} ({src_ip} | SN {req.serial_no})") + return SaoNoopResponse(GameconnectCmd.ADD_CREDIT_GUEST_RESPONSE).make() + + async def handle_c11c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/consume_credit_guest + req = SaoConsumeCreditGuestRequest(header, request) + self.logger.info(f"Guest consumed {req.consume_num} credits on a {'game' if req.cab_type == 0 else 'terminal'} @ {req.shop_id} ({src_ip} | SN {req.serial_num}) on {req.act_type}") + return SaoNoopResponse(GameconnectCmd.CONSUME_CREDIT_GUEST_RESPONSE).make() + + async def handle_c11e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #common/get_auth_card_data + req = SaoGetAuthCardDataRequest(header, request) + + #Check authentication + card = await self.data.card.get_card_by_access_code( req.access_code ) + + if not card: + card = await self.data.card.get_card_by_idm(req.chip_id[:16]) + if not card: + self.logger.info(f"Unregistered card {req.access_code} authenticated from {req.serial_no}") + return SaoGetAuthCardDataResponse("NEW PLAYER", 0).make() + + await self.data.card.set_access_code_by_access_code(card['access_code'], req.access_code) + + else: + user_id = card['user'] + card_id = card['id'] + + if req.access_code.startswith("5") and not card['idm']: + await self.data.card.set_idm_by_access_code(card_id, req.chip_id[:16]) + elif (req.access_code.startswith("010") or req.access_code.startswith("3")) and not card['chip_id'] and int(req.chip_id[:8], 16) != 0x04030201: + await self.data.card.set_chip_id_by_access_code(card_id, int(req.chip_id[:8], 16)) + + self.logger.info(f"User Authenticated from {req.serial_no}: { req.access_code } | { user_id }") #Grab values from profile profile_data = await self.data.profile.get_profile(user_id) - if user_id and not profile_data: - profile_id = await self.data.profile.create_profile(user_id) - await self.data.item.put_hero_log(user_id, 101000010, 1, 0, 201000000, 0, 1002, 1003, 1014, 30001, 30310) - await self.data.item.put_hero_log(user_id, 102000010, 1, 0, 202000000, 0, 3001, 3002, 3004, 30007, 3011) - await self.data.item.put_hero_log(user_id, 105000010, 1, 0, 209000000, 0, 10005, 10002, 10004, 30006, 10003) - await self.data.item.put_hero_log(user_id, 101000110, 1, 0, 201000000, 101000110, 2002, 2001, 2014, 0, 0) - await self.data.item.put_hero_party(user_id, 0, 101000010, 102000010, 105000010) - await self.data.item.put_equipment_data(user_id, 201000000, 1, 200, 0, 0, 0) - await self.data.item.put_equipment_data(user_id, 202000000, 1, 200, 0, 0, 0) - await self.data.item.put_equipment_data(user_id, 209000000, 1, 200, 0, 0, 0) - await self.data.item.put_equipment_data(user_id, 101000110, 1, 200, 0, 0, 0) - await self.data.item.put_player_quest(user_id, 1001, True, 300, 0, 0, 1) + if not profile_data: + self.logger.info(f"Unregistered user {user_id} with card {req.access_code} authenticated from {req.serial_no}") + return SaoGetAuthCardDataResponse("NEW PLAYER", user_id).make() - # Force the tutorial stage to be completed due to potential crash in-game + return SaoGetAuthCardDataResponse(profile_data['nick_name'], user_id).make() + async def handle_c120(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/get_access_code_by_keitai + req = SaoGetAccessCodeByKeitaiRequest(header, request) + cid = req.chip_id + idm = cid[:16] + pmm = cid[16:] + card = await self.data.card.get_card_by_idm(idm) + + # If we don't have that card saved locally, check aimedb + if not card: + # Validate that we're talking to a phone + if not int(pmm[2:4], 16) in self.data.card.moble_os_codes: + self.logger.warn(f"{req.serial_no} looked up non-moble chip ID {cid}!") + return SaoGetAccessCodeByKeitaiResponse("").make() + + # TODO: Actual felica moble registration + return SaoGetAccessCodeByKeitaiResponse("").make() + #ac = await self.data.card.register_felica_moble_ac(idm, pmm) + # if we didn't get an access code, fail hard + if not ac: + self.logger.warn(f"Failed to register access code for chip ID {cid} requested by {req.serial_no}") + return SaoGetAccessCodeByKeitaiResponse("").make() + + self.logger.info(f"Successfully registered moble felica access code {ac} for chip ID {cid} requested by {req.serial_no}") + + uid = await self.data.user.create_user() + if not uid: + self.logger.error(f"Failed to create user for chip ID {cid} (access code {ac}) @ LoadAccessCode request from {req.serial_no}") + return SaoGetAccessCodeByKeitaiResponse("").make() + + cardid = await self.data.card.create_card(uid, ac) + if not cardid: + self.logger.error(f"Failed to create card for user {uid} with chip ID {cid} (access code {ac}) @ LoadAccessCode request from {req.serial_no}") + await self.data.user.delete_user(uid) + return SaoGetAccessCodeByKeitaiResponse("").make() + + self.logger.info(f"Moble Felica access code lookup for {cid} -> {ac} (user {uid}) requested by {req.serial_no}") + + else: + ac = card['access_code'] + uid = card['user'] + self.logger.info(f"Moble Felica access code for {cid} -> {ac} (user {uid}) requested by {req.serial_no}") + + return SaoGetAccessCodeByKeitaiResponse(ac).make() - profile_data = await self.data.profile.get_profile(user_id) - - resp = SaoGetAuthCardDataResponse(header.cmd +1, profile_data) + async def handle_c122(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #common/get_maintenance_info + resp = SaoGetMaintenanceInfoResponse() return resp.make() - async def handle_c40c(self, header: SaoRequestHeader, request: bytes) -> bytes: - #home/check_ac_login_bonus - resp = SaoHomeCheckAcLoginBonusResponse(header.cmd +1) + async def handle_c124(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #common/get_resource_path_info + resp = SaoGetResourcePathInfoResponse(f"https://{self.core_cfg.server.hostname}/saoresource/") return resp.make() - async def handle_c104(self, header: SaoRequestHeader, request: bytes) -> bytes: - #common/login - req = SaoCommonLoginRequest(header, request) + async def handle_c126(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/validation_error_notification + req = SaoValidationErrorNotificationRequest(header, request) + self.logger.warn(f"User {req.user_id} on {'game' if req.cabinet_type == 0 else 'terminal'} {req.serial_no} @ {req.store_name} ({src_ip} | Place ID {req.place_id}) " \ + + f"Validation error: {req.send_protocol_name} || {req.send_data_to_fraud_value} || {req.send_data_to_modification_value}") + return SaoNoopResponse(GameconnectCmd.VALIDATION_ERROR_NOTIFICATION_RESPONSE).make() - user_id = await self.data.card.get_user_id_from_card( req.access_code ) - profile_data = await self.data.profile.get_profile(user_id) + async def handle_c128(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/power_cutting_return_notification + req = SaoPowerCuttingReturnNotification(header, request) + self.logger.warn(f"User {req.user_id} on {'game' if req.cabinet_type == 0 else 'terminal'} {req.serial_no} @ {req.store_name} ({src_ip} | Place ID {req.place_id}) " \ + + f"Power outage return: Act Type {req.last_act_type} || {req.remaining_ticket_num} Remaining Tickets || {req.remaining_credit_num} Remaining Credits") + return SaoNoopResponse(GameconnectCmd.POWER_CUTTING_RETURN_NOTIFICATION_RESPONSE).make() - resp = SaoCommonLoginResponse(header.cmd +1, profile_data) + async def handle_c12a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #common/give_free_ticket + req = SaoGiveFreeTicketRequest(header, request) + self.logger.info(f"Give {req.give_num} free tickets (id {req.ticket_id}) to user {req.user_id}") + resp = SaoNoopResponse(GameconnectCmd.GIVE_FREE_TICKET_RESPONSE) return resp.make() - async def handle_c404(self, header: SaoRequestHeader, request: bytes) -> bytes: - #home/check_comeback_event - resp = SaoCheckComebackEventRequest(header.cmd +1) + async def handle_c12c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # common/matching_error_notification + req = SaoMatchingErrorNotificationRequest(header, request) + self.logger.warn(f"{'game' if req.cabinet_type == 0 else 'terminal'} {req.serial_no} @ {req.store_name} ({src_ip} | Place ID {req.place_id}) " \ + + f"Matching error: {req.matching_error_data_list[0]}") + return SaoNoopResponse(GameconnectCmd.MATCHING_ERROR_NOTIFICATION_RESPONSE).make() + + async def handle_c12e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #common/ac_cabinet_boot_notification + req = SaoCommonAcCabinetBootNotificationRequest(header, request) + + if req.current_version_app_id < self.game_cfg.server.game_version: + self.logger.info(f"!!OUTDATED!! {'Game' if req.cabinet_type == 0 else 'Terminal'} {req.serial_no} Booted v{req.current_version_app_id} (Master data v{req.current_master_data_version}): {req.store_name} ({src_ip} | Place/Shop ID {req.place_id}/{req.store_id})") + + if req.current_version_app_id > self.game_cfg.server.game_version: + self.logger.info(f"!!TOO NEW!! {'Game' if req.cabinet_type == 0 else 'Terminal'} {req.serial_no} Booted v{req.current_version_app_id} (Master data v{req.current_master_data_version}): {req.store_name} ({src_ip} | Place/Shop ID {req.place_id}/{req.store_id})") + + self.logger.info(f"{'Game' if req.cabinet_type == 0 else 'Terminal'} {req.serial_no} Booted v{req.current_version_app_id} (Master data v{req.current_master_data_version}): {req.store_name} ({src_ip} | Place/Shop ID {req.place_id}/{req.store_id})") + resp = SaoNoopResponse(GameconnectCmd.AC_CABINET_BOOT_NOTIFICATION_RESPONSE) return resp.make() - async def handle_c000(self, header: SaoRequestHeader, request: bytes) -> bytes: - #ticket/ticket - resp = SaoTicketResponse(header.cmd +1) + async def handle_c200(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # tutorial/first_tutorial_end + req = SaoGenericUserTicketRequest(header, request) + self.logger.info(f"User {req.user_id} (ticket {req.ticket_id}) finished first tutorial") + return SaoNoopResponse(GameconnectCmd.FIRST_TUTORIAL_END_RESPONSE).make() + + async def handle_c202(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # tutorial/various_tutorial_end + req = SaoVariousTutorialEndRequest(header, request) + await self.data.profile.add_tutorial_byte(int(req.user_id), req.tutorial_type) + return SaoNoopResponse(GameconnectCmd.VARIOUS_TUTORIAL_END_RESPONSE).make() + + async def handle_c204(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # tutorial/get_various_tutorial_data_list + req = SaoGenericUserRequest(header, request) + tuts = await self.data.profile.get_tutorial_bytes(int(req.user_id)) + resp = SaoGetVariousTutorialDataListResponse() + if tuts: + for t in tuts: + resp.end_tutorial_type_list.append(t['tutorial_byte']) + return resp.make() - async def handle_c500(self, header: SaoRequestHeader, request: bytes) -> bytes: - #user_info/get_user_basic_data + async def handle_c300(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # card/discharge_profile_card + req = SaoDischargeProfileCardRequest(header, request) + # Real cards seem to start with 10-17 as the first 2 digits, so we'll anchor ours with 2 to ensure no overlap + sn = f"2{str(randint(1, 999999999999999999)).zfill(18)}" + while await self.data.profile.get_hero_card(sn): + sn = f"2{str(randint(1, 999999999999999999)).zfill(18)}" + resp = SaoDischargeProfileCardResponse(sn) + + db_hero = await self.data.item.get_hero_log(req.user_id, req.hero_log_user_hero_log_id) + if not db_hero: + hero_statc = await self.data.static.get_hero_by_id(db_hero['hero_log_id']) + if not hero_statc: + self.logger.error(f"Failed to find hero log {db_hero['hero_log_id']}! Please run the reader") + resp.header.error_type = ProtocolErrorNum.RESOURCE_CARD_ERR1 + return resp.make() + + now_have_skills = await self.hero_default_skills(hero_statc['SkillTableSubId']) + + db_hero_id = await self.data.item.put_hero_log( + req.user_id, + db_hero['hero_log_id'], + 1, + 0, + None, + None, + now_have_skills[0], + now_have_skills[1], + now_have_skills[2], + now_have_skills[3], + now_have_skills[4] + ) + if not db_hero_id: + self.logger.error(f"Failed to give user {req.user_id} hero {db_hero['hero_log_id']}!") + resp.header.error_type = ProtocolErrorNum.RESOURCE_CARD_ERR6 + return resp.make() + + else: + db_hero_id = db_hero['id'] + + await self.data.profile.put_hero_card(req.user_id, sn, db_hero_id, req.holographic_flag) + + self.logger.info(f"User {req.user_id} printed {'holo ' if req.holographic_flag == 1 else ''}profile card {req.hero_log_user_hero_log_id} {req.execute_print_type}, code {sn}") + return resp.make() + + async def handle_c302(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # card/discharge_resource_card + req = SaoDischargeResourceCardRequest(header, request) + + for x in req.common_reward_user_data: + sn = f"2{str(randint(1, 999999999999999999)).zfill(18)}" + self.logger.info(f"User {req.user_id} printed {'holo ' if req.holographic_flag == 1 else ''}resource card {x.user_common_reward_id} {req.execute_print_type}, code {sn}") + await self.data.profile.put_resource_card(req.user_id, sn, x.common_reward_type, x.user_common_reward_id, req.holographic_flag) + + return SaoDischargeProfileCardResponse(sn).make() + + async def handle_c304(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # card/discharge_resource_card_complete + req = SaoDischargeResourceCardCompleteRequest(header, request) + self.logger.info(f"User {req.user_id} finished printing resource card {req.resource_card_code}") + return SaoNoopResponse(GameconnectCmd.DISCHARGE_RESOURCE_CARD_COMPLETE_RESPONSE).make() + + async def handle_c306(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #card/scan_qr_quest_profile_card + req = SaoScanQrQuestProfileCardRequest(header, request) + resp = SaoScanQrQuestProfileCardResponse() + + card = await self.data.profile.get_hero_card(req.profile_card_code) + if not card: + self.logger.warn(f"User {req.user_id} scanned unregistered QR code {req.profile_card_code}") + return resp.make() + + hero = await self.data.item.get_hero_log_by_id(card['user_hero_id']) + if not hero: # Shouldn't happen + self.logger.warn(f"User {req.user_id} scanned QR code {req.profile_card_code} but does not have hero entry {card['user_hero_id']}") + return resp.make() + + hero_static_data = await self.data.static.get_hero_by_id(hero['hero_log_id']) + if not hero_static_data: # Shouldn't happen + self.logger.warn(f"No entry for hero {hero['hero_log_id']}, please run read.py") + return resp.make() + + profile = await self.data.profile.get_profile(card['user']) + if not profile: # Shouldn't happen + self.logger.warn(f"No profile for user {card['user']}, something broke") + return resp.make() + + self.logger.info(f"User {req.user_id} scanned QR code {req.profile_card_code}") + card_resp = ReadProfileCard.from_args(req.profile_card_code, profile['nick_name']) + card_resp.rank_num = profile['rank_num'] + card_resp.setting_title_id = profile['setting_title_id'] + card_resp.skill_id = hero['skill_slot1_skill_id'] + card_resp.hero_log_hero_log_id = hero['hero_log_id'] + card_resp.hero_log_log_level = hero['log_level'] + #TODO: Awakening + #card_resp.hero_log_awakening_stage = hero['log_level'] + + resp.profile_card_data.append(card_resp) + return resp.make() + + async def handle_c308(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # card/scan_qr_shop_resource_card + req = SaoScanQrShopResourceCardRequest(header, request) + self.logger.info(f"User {req.user_id} scanned shop resource card {req.resource_card_code}") + resp = SaoNoopResponse(GameconnectCmd.SCAN_QR_SHOP_RESOURCE_CARD_RESPONSE) + # On official, resource cards have limited uses, but we don't track that currently (tho we should) + + card = await self.data.profile.get_resource_card(req.resource_card_code) # TODO: use count + if not card: + self.logger.warn(f"No resource card with serial {req.resource_card_code} exists!") + resp.header.err_status = 4832 # Theres a few error codes but none seem to do anything? + # Also not sure if it should be this or result + + return resp.make() + + async def handle_c30a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # card/scan_qr_quest_resource_card + req = SaoScanQrQuestResourceCardRequest(header, request) + self.logger.info(f"User {req.user_id} scanned quest resource card {req.resource_card_code}") + + card = await self.data.profile.get_resource_card(req.resource_card_code) + if not card: + resp = SaoScanQrQuestResourceCardResponse(card['common_reward_type'], card['common_reward_id'], card['holographic_flag']) + + else: + self.logger.warn(f"No resource card with serial {req.resource_card_code} exists!") + resp = SaoScanQrQuestResourceCardResponse() + resp.header.err_status = 4832 # Theres a few error codes but none seem to do anything? + # Also not sure if it should be this or result + + return resp.make() + + async def handle_c400(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #home/check_yui_medal_get_condition + req = SaoCheckYuiMedalGetConditionRequest(header, request) + profile = await self.data.profile.get_profile(req.user_id) + if profile['last_yui_medal_date']: + last_check_ts = int(profile['last_yui_medal_date'].timestamp()) + day_ts = int(datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).timestamp()) + diff_ts = day_ts - last_check_ts + if diff_ts > 0: + num_days = diff_ts / 86400 + else: + num_days = 0 + else: + num_days = 1 + + if num_days > 1: + await self.data.profile.add_yui_medals(req.user_id) + + await self.data.profile.update_yui_medal_date(req.user_id) + return SaoCheckYuiMedalGetConditionResponse(num_days, 1 if num_days > 1 else 0).make() + + async def handle_c402(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #home/get_yui_medal_bonus_user_data + req = SaoGetYuiMedalBonusUserDataRequest(header, request) + resp = SaoGetYuiMedalBonusUserDataResponse() # TODO: Track yui login bonus + return resp.make() + + async def handle_c404(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # home/check_comeback_event + req = SaoGenericUserTicketRequest(header, request) + resp = SaoCheckComebackEventResponse() + #resp.get_comeback_event_id_list += [1,2,3,4] # TODO: track comeback date + return resp.make() + + async def handle_c406(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # home/change_my_store + req = SaoChangeMyStoreRequest(header, request) + self.logger.info(f"User {req.user_id} changed My Store to {req.store_id}") + shop_id = int(req.store_id[3:], 16) + await self.data.profile.set_my_shop(req.user_id, shop_id) + return SaoNoopResponse(GameconnectCmd.CHANGE_MY_STORE_RESPONSE).make() + + async def handle_c408(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # home/check_title_get_decision + req = SaoCheckTitleGetDecisionRequest(header, request) + resp = SaoCheckTitleGetDecisionResponse() # TODO: titles + return resp.make() + + async def handle_c40a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # home/check_profile_card_used_reward + req = SaoCheckProfileCardUsedRewardRequest(header, request) + resp = SaoCheckProfileCardUsedRewardResponse() # TODO: check_profile_card_used_reward + return resp.make() + + async def handle_c40c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # home/check_ac_login_bonus + req = SaoGenericUserTicketRequest(header, request) + resp = SaoCheckAcLoginBonusResponse() + #resp.get_ac_login_bonus_id_list.append(1) # TODO: track login bonus date + #resp.get_ac_login_bonus_id_list.append(2) + return resp.make() + + async def handle_c500(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # user_info/get_user_basic_data req = SaoGetUserBasicDataRequest(header, request) profile_data = await self.data.profile.get_profile(req.user_id) + player_rank_data = self.load_data_csv("PlayerRank") - resp = SaoGetUserBasicDataResponse(header.cmd +1, profile_data) - return resp.make() + resp = SaoGetUserBasicDataResponse(profile_data) + for e in player_rank_data: + if resp.user_basic_data[0].rank_num == int(e['PlayerRankId']): + resp.user_basic_data[0].rank_exp = resp.user_basic_data[0].rank_exp - int(e["TotalExp"]) + break - async def handle_c600(self, header: SaoRequestHeader, request: bytes) -> bytes: - #have_object/get_hero_log_user_data_list - req = SaoGetHeroLogUserDataListRequest(header, request) + if profile_data['my_shop']: + ac = await self.data.arcade.get_arcade(profile_data['my_shop']) + if ac: + resp.user_basic_data[0].my_store_name = ac['name'] + + return resp.make() + + async def handle_c502(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # user_info/get_vp_gasha_ticket_data_list + req = SaoGetVpGashaTicketDataListRequest(header, request) + # TODO: gasha tickets + resp = SaoGetVpGashaTicketDataListResponse() + return resp.make() + + async def handle_c504(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # user_info/get_present_box_num + req = SaoGenericUserRequest(header, request) + # TODO: presents + return SaoGetPresentBoxNumResponse().make() + + async def handle_c600(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # have_object/get_hero_log_user_data_list + req = SaoGenericUserRequest(header, request) + hero_level_data = self.load_data_csv("HeroLogLevel") hero_data = await self.data.item.get_hero_logs(req.user_id) - resp = SaoGetHeroLogUserDataListResponse(header.cmd +1, hero_data) + resp = SaoGetHeroLogUserDataListResponse() + for hero in hero_data: + append = HeroLogUserData.from_args(hero) + hero_static = await self.data.static.get_hero_by_id(hero['hero_log_id']) + if not hero_static: + self.logger.warn(f"No hero for id {hero['hero_log_id']}, please run reader") + resp.hero_log_user_data_list.append(append) + continue + + append.property1_property_id = hero_static['Property1PropertyId'] if hero_static['Property1PropertyId'] else 0 + append.property1_value1 = hero_static['Property1Value1'] if hero_static['Property1Value1'] else 0 + append.property1_value2 = hero_static['Property1Value2'] if hero_static['Property1Value2'] else 0 + append.property2_property_id = hero_static['Property2PropertyId'] if hero_static['Property2PropertyId'] else 0 + append.property2_value1 = hero_static['Property2Value1'] if hero_static['Property2Value1'] else 0 + append.property2_value2 = hero_static['Property2Value2'] if hero_static['Property2Value2'] else 0 + append.property3_property_id = hero_static['Property3PropertyId'] if hero_static['Property3PropertyId'] else 0 + append.property3_value1 = hero_static['Property3Value1'] if hero_static['Property3Value1'] else 0 + append.property3_value2 = hero_static['Property3Value2'] if hero_static['Property3Value2'] else 0 + append.property4_property_id = hero_static['Property4PropertyId'] if hero_static['Property4PropertyId'] else 0 + append.property4_value1 = hero_static['Property4Value1'] if hero_static['Property4Value1'] else 0 + append.property4_value2 = hero_static['Property4Value2'] if hero_static['Property4Value2'] else 0 + + for e in hero_level_data: + if hero['log_level'] == int(e['HeroLogLevelId']): + append.log_exp = hero['log_exp'] - int(e["TotalExp"]) + break + resp.hero_log_user_data_list.append(append) + return resp.make() - - async def handle_c602(self, header: SaoRequestHeader, request: bytes) -> bytes: - #have_object/get_equipment_user_data_list - req = SaoGetEquipmentUserDataListRequest(header, request) + + async def handle_c602(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # have_object/get_equipment_user_data_list + req = SaoGenericUserRequest(header, request) + resp = SaoGetEquipmentUserDataListResponse() + equip_level_data = self.load_data_csv("EquipmentLevel") equipment_data = await self.data.item.get_user_equipments(req.user_id) + + if equipment_data: + for equipment in equipment_data: + e = EquipmentUserData.from_args(equipment) + weapon_static = await self.data.static.get_equipment_by_id(equipment['equipment_id']) + if not weapon_static: + self.logger.warn(f"No equipment for id {equipment['equipment_id']}, please run reader") + resp.equipment_user_data_list.append(e) + continue + + if not e.property1_property_id: + e.property1_property_id = weapon_static['Property1PropertyId'] if weapon_static['Property1PropertyId'] else 0 + e.property1_value1 = weapon_static['Property1Value1'] if weapon_static['Property1Value1'] else 0 + e.property1_value2 = weapon_static['Property1Value2'] if weapon_static['Property1Value2'] else 0 + + if not e.property2_property_id: + e.property2_property_id = weapon_static['Property2PropertyId'] if weapon_static['Property2PropertyId'] else 0 + e.property2_value1 = weapon_static['Property2Value1'] if weapon_static['Property2Value1'] else 0 + e.property2_value2 = weapon_static['Property2Value2'] if weapon_static['Property2Value2'] else 0 + + if e.property3_property_id: + e.property3_property_id = weapon_static['Property3PropertyId'] if weapon_static['Property3PropertyId'] else 0 + e.property3_value1 = weapon_static['Property3Value1'] if weapon_static['Property3Value1'] else 0 + e.property3_value2 = weapon_static['Property3Value2'] if weapon_static['Property3Value2'] else 0 + + if e.property4_property_id: + e.property4_property_id = weapon_static['Property4PropertyId'] if weapon_static['Property4PropertyId'] else 0 + e.property4_value1 = weapon_static['Property4Value1'] if weapon_static['Property4Value1'] else 0 + e.property4_value2 = weapon_static['Property4Value2'] if weapon_static['Property4Value2'] else 0 + + for f in equip_level_data: + if equipment['enhancement_value'] == int(f['EquipmentLevelId']): + e.enhancement_exp = equipment['enhancement_exp'] - int(f["TotalExp"]) + break + resp.equipment_user_data_list.append(e) - resp = SaoGetEquipmentUserDataListResponse(header.cmd +1, equipment_data) return resp.make() - - async def handle_c604(self, header: SaoRequestHeader, request: bytes) -> bytes: - #have_object/get_item_user_data_list - req = SaoGetItemUserDataListRequest(header, request) + + async def handle_c604(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # have_object/get_item_user_data_list + req = SaoGenericUserRequest(header, request) item_data = await self.data.item.get_user_items(req.user_id) - resp = SaoGetItemUserDataListResponse(header.cmd +1, item_data) + resp = SaoGetItemUserDataListResponse(item_data) return resp.make() + + async def handle_c606(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # have_object/get_support_log_user_data_list + req = SaoGenericUserRequest(header, request) + resp = SaoGetSupportLogUserDataListResponse() + supports = self.load_data_csv("SupportLog") + # TODO: Save supports - async def handle_c606(self, header: SaoRequestHeader, request: bytes) -> bytes: - #have_object/get_support_log_user_data_list - supportIdsData = await self.data.static.get_support_log_ids(0, True) - - resp = SaoGetSupportLogUserDataListResponse(header.cmd +1, supportIdsData) + for x in range(len(supports)): + tmp = SupportLogUserData.from_args(f"{req.user_id}{x}", supports[x]['SupportLogId']) + resp.support_log_user_data_list.append(tmp) + return resp.make() + + async def handle_c608(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #have_object/get_episode_append_data_list + req = SaoGenericUserRequest(header, request) + # TODO: Appends + resp = SaoGetEpisodeAppendDataListResponse() return resp.make() - async def handle_c800(self, header: SaoRequestHeader, request: bytes) -> bytes: - #custom/get_title_user_data_list + async def handle_c60a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # have_object/get_event_item_data_list + req = SaoGenericUserRequest(header, request) + res = SaoGetEventItemDataListResponse() + # TODO: Event items maybe + return res.make() + + async def handle_c60c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # have_object/get_gasha_medal_user_data_list + req = SaoGenericUserRequest(header, request) + res = SaoGetGashaMedalUserDataListResponse() + # TODO: Gasha Medal data + return res.make() + + async def handle_c700(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # shop/get_shop_resource_sales_data_list + # TODO: Get user shop data + req = SaoGenericUserRequest(header, request) + resp = SaoGetShopResourceSalesDataListResponse() + return resp.make() + + async def handle_c702(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # shop/purchase_shop_resource + req = SaoPurchaseShopResourceRequest(header, request) + self.logger.infof(f"User {req.user_id} (ticket {req.ticket_id}) purchased shop resourse {req.user_shop_resource_id}") + # TODO: Shop purchases + return SaoNoopResponse(GameconnectCmd.PURCHASE_SHOP_RESOURCE_RESPONSE).make() + + async def handle_c704(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # shop/discard_shop_resource + req = SaoPurchaseShopResourceRequest(header, request) + self.logger.infof(f"User {req.user_id} (ticket {req.ticket_id}) discarded shop resourse {req.user_shop_resource_id}") + return SaoNoopResponse(GameconnectCmd.DISCARD_SHOP_RESOURCE_RESPONSE).make() + + async def handle_c800(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # custom/get_title_user_data_list + req = SaoGenericUserRequest(header, request) titleIdsData = await self.data.static.get_title_ids(0, True) + # TODO: Save titles - resp = SaoGetTitleUserDataListResponse(header.cmd +1, titleIdsData) + resp = SaoGetTitleUserDataListResponse(req.user_id, titleIdsData) return resp.make() + + async def handle_c802(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # custom/change_title + req = SaoChangeTitleRequest(header, request) + self.logger.info(f"User {req.user_id} (ticket {req.ticket_id}) changed their title to {req.user_title_id}") + await self.data.profile.set_title(req.user_id, req.user_title_id) - async def handle_c608(self, header: SaoRequestHeader, request: bytes) -> bytes: - #have_object/get_episode_append_data_list - req = SaoGetEpisodeAppendDataListRequest(header, request) + return SaoNoopResponse(GameconnectCmd.CHANGE_TITLE_RESPONSE).make() - profile_data = await self.data.profile.get_profile(req.user_id) + async def handle_c804(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # custom/get_party_data_list + req = SaoGenericUserRequest(header, request) + resp = SaoGetPartyDataListResponse() + + hero_parties = await self.data.item.get_hero_party(req.user_id) + for party in hero_parties: + hero1_data = await self.data.item.get_user_hero_by_id(party['user_hero_log_id_1']) + hero2_data = await self.data.item.get_user_hero_by_id(party['user_hero_log_id_2']) + hero3_data = await self.data.item.get_user_hero_by_id(party['user_hero_log_id_3']) + + resp.party_data_list.append(PartyData.from_args(party['id'], party['user_party_team_id'], hero1_data._asdict(), hero2_data._asdict(), hero3_data._asdict())) - resp = SaoGetEpisodeAppendDataListResponse(header.cmd +1, profile_data) return resp.make() - async def handle_c804(self, header: SaoRequestHeader, request: bytes) -> bytes: - #custom/get_party_data_list - req = SaoGetPartyDataListRequest(header, request) - - hero_party = await self.data.item.get_hero_party(req.user_id, 0) - hero1_data = await self.data.item.get_hero_log(req.user_id, hero_party[3]) - hero2_data = await self.data.item.get_hero_log(req.user_id, hero_party[4]) - hero3_data = await self.data.item.get_hero_log(req.user_id, hero_party[5]) - - resp = SaoGetPartyDataListResponse(header.cmd +1, hero1_data, hero2_data, hero3_data) + async def handle_c808(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # custom/get_support_log_party_data_list + req = SaoGenericUserRequest(header, request) + resp = SaoGetSupportLogPartyDataListResponse() + # TODO: Support logs return resp.make() - async def handle_c902(self, header: SaoRequestHeader, request: bytes) -> bytes: # for whatever reason, having all entries empty or filled changes nothing - #quest/get_quest_scene_prev_scan_profile_card - resp = SaoGetQuestScenePrevScanProfileCardResponse(header.cmd +1) - return resp.make() + async def handle_c812(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # custom/disposal_resource + req = SaoDisposalResourceRequest(header, request) + get_col = 0 + for disposal in req.disposal_common_reward_user_data_list: + if disposal.common_reward_type == RewardType.HeroLog: + removed_hero = await self.data.item.remove_hero_log(disposal.user_common_reward_id) + if removed_hero: + static_hero = await self.data.static.get_hero_by_id(removed_hero) + get_col += static_hero['SalePrice'] + + elif disposal.common_reward_type == RewardType.Equipment: + removed_equip = await self.data.item.remove_equipment(disposal.user_common_reward_id) + if removed_equip: + static_equip = await self.data.static.get_equipment_by_id(removed_equip) + get_col += static_equip['SalePrice'] - async def handle_c124(self, header: SaoRequestHeader, request: bytes) -> bytes: - #common/get_resource_path_info - resp = SaoGetResourcePathInfoResponse(header.cmd +1) - return resp.make() + elif disposal.common_reward_type == RewardType.Item: + removed_equip = await self.data.item.remove_item(disposal.user_common_reward_id) + if removed_equip: + static_equip = await self.data.static.get_equipment_by_id(removed_equip) + get_col += static_equip['SalePrice'] - async def handle_c900(self, header: SaoRequestHeader, request: bytes) -> bytes: - #quest/get_quest_scene_user_data_list // QuestScene.csv - req = SaoGetQuestSceneUserDataListRequest(header, request) + elif disposal.common_reward_type == RewardType.SupportLog: + continue - quest_data = await self.data.item.get_quest_logs(req.user_id) + else: + self.logger.warn(f"Unhandled disposal type {disposal.common_reward_type}") - resp = SaoGetQuestSceneUserDataListResponse(header.cmd +1, quest_data) - return resp.make() + await self.data.profile.add_col(req.user_id, get_col) + return SaoDisposalResourceResponse(get_col).make() - async def handle_c400(self, header: SaoRequestHeader, request: bytes) -> bytes: - #home/check_yui_medal_get_condition - resp = SaoCheckYuiMedalGetConditionResponse(header.cmd +1) - return resp.make() - - async def handle_c402(self, header: SaoRequestHeader, request: bytes) -> bytes: - #home/get_yui_medal_bonus_user_data - resp = SaoGetYuiMedalBonusUserDataResponse(header.cmd +1) - return resp.make() - - async def handle_c40a(self, header: SaoRequestHeader, request: bytes) -> bytes: - #home/check_profile_card_used_reward - resp = SaoCheckProfileCardUsedRewardResponse(header.cmd +1) - return resp.make() - - async def handle_c814(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c814(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #custom/synthesize_enhancement_hero_log req = SaoSynthesizeEnhancementHeroLogRequest(header, request) - - synthesize_hero_log_data = await self.data.item.get_hero_log(req.user_id, req.origin_user_hero_log_id) + resp = SaoSynthesizeEnhancementHeroLogResponse() + hero_level_data = self.load_data_csv("HeroLogLevel") + equip_level_data = self.load_data_csv("EquipmentLevel") + hero_exp = 0 + col_cost = 0 for x in req.material_common_reward_user_data_list: - hero_exp = 0 - itemList = await self.data.static.get_item_id(x.user_common_reward_id) - heroList = await self.data.static.get_hero_id(x.user_common_reward_id) - equipmentList = await self.data.static.get_equipment_id(x.user_common_reward_id) + if x.common_reward_type == RewardType.Item: + user_item = await self.data.item.get_user_item_by_id(x.user_common_reward_id) + static_item = await self.data.static.get_item_id(user_item['item_id']) + + if int(static_item['ItemTypeId']) == 7: + hero_exp += int(static_item['Value']) + self.logger.info(f"Remove item {x.user_common_reward_id} and add {int(static_item['Value'])} XP (running {hero_exp})") + await self.data.item.remove_item(x.user_common_reward_id) - if itemList: - hero_exp = 2000 + int(synthesize_hero_log_data["log_exp"]) - await self.data.item.remove_item(req.user_id, x.user_common_reward_id) - - if equipmentList: - equipment_data = await self.data.item.get_user_equipment(req.user_id, x.user_common_reward_id) + elif x.common_reward_type == RewardType.Equipment: + equipment_data = await self.data.item.get_user_equipment_by_id(x.user_common_reward_id) if equipment_data is None: self.logger.error(f"Failed to find equipment {x.user_common_reward_id} for user {req.user_id}!") continue - hero_exp = int(equipment_data["enhancement_exp"]) + int(synthesize_hero_log_data["log_exp"]) - await self.data.item.remove_equipment(req.user_id, x.user_common_reward_id) + req_exp = 0 + for e in range(len(equip_level_data)): + if equipment_data['enhancement_value'] == int(equip_level_data[e]['EquipmentLevelId']): + req_exp = equip_level_data[e + 1]['RequireExp'] + break + + static_equip_data = await self.data.static.get_equipment_by_id(equipment_data['equipment_id']) + + hero_exp += int(static_equip_data['CompositionExp']) + req_exp + self.logger.info(f"Remove equipment {x.user_common_reward_id} and add {int(static_equip_data['CompositionExp']) + req_exp} XP (running {hero_exp})") + await self.data.item.remove_equipment(x.user_common_reward_id) - if heroList: - hero_data = await self.data.item.get_hero_log(req.user_id, x.user_common_reward_id) + elif x.common_reward_type == RewardType.HeroLog: + hero_data = await self.data.item.get_hero_log_by_id(x.user_common_reward_id) if hero_data is None: self.logger.error(f"Failed to find hero {x.user_common_reward_id} for user {req.user_id}!") continue + + req_exp = 0 + for e in range(len(hero_level_data)): + if hero_data['log_level'] == int(hero_level_data[e]['HeroLogLevelId']): + req_exp = hero_level_data[e + 1]['RequireExp'] + break - hero_exp = int(hero_data["log_exp"]) + int(synthesize_hero_log_data["log_exp"]) - await self.data.item.remove_hero_log(req.user_id, x.user_common_reward_id) + static_hero_data = await self.data.static.get_hero_by_id(hero_data['hero_log_id']) + + hero_exp += int(static_hero_data['CompositionExp']) + req_exp + self.logger.info(f"Remove hero {x.user_common_reward_id} and add {int(static_hero_data['CompositionExp']) + req_exp} XP (running {hero_exp})") + await self.data.item.remove_hero_log(x.user_common_reward_id) - if hero_exp == 0: - self.logger.warn(f"Hero {x.user_common_reward_id} (type {x.common_reward_type}) not found!") + else: + self.logger.warn(f"Unhandled ype {x.common_reward_type}! (running {hero_exp})") + + hero_exp = int(hero_exp * 1.5) + await self.data.item.add_hero_xp(req.origin_user_hero_log_id, hero_exp) + log_exp = await self.data.item.get_hero_xp(req.origin_user_hero_log_id) + pre_synth_xp = log_exp - hero_exp + pre_synth_level = 1 + + for e in range(len(hero_level_data)): + if log_exp>=int(hero_level_data[e]["TotalExp"]) and log_exp=int(hero_level_data[e]["TotalExp"]) and pre_synth_xp= 100: + col_cost = col_cost * 10 + + col_cost = col_cost * 1000 + col_cost = max(100, int(col_cost / 1000)) + + self.logger.info(f"Synthesize {hero_exp} exp for hero {req.origin_user_hero_log_id}, costing {col_cost} col") + + await self.data.profile.add_col(req.user_id, -col_cost) + if synthesize_hero_log_data is not None: + resp.after_hero_log_user_data.append(HeroLogUserData.from_args(synthesize_hero_log_data)) + resp.after_hero_log_user_data[0].log_exp = remain_exp return resp.make() - async def handle_c816(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c816(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #custom/synthesize_enhancement_equipment req_data = SaoSynthesizeEnhancementEquipmentRequest(header, request) - synthesize_equipment_data = await self.data.item.get_user_equipment(req_data.user_id, req_data.origin_user_equipment_id) + resp = SaoSynthesizeEnhancementEquipmentResponse() + hero_level_data = self.load_data_csv("HeroLogLevel") + equip_level_data = self.load_data_csv("EquipmentLevel") + equipment_exp = 0 + col_cost = 0 for x in req_data.material_common_reward_user_data_list: - equipment_exp = 0 - itemList = await self.data.static.get_item_id(x.user_common_reward_id) - heroList = await self.data.static.get_hero_id(x.user_common_reward_id) - equipmentList = await self.data.static.get_equipment_id(x.user_common_reward_id) + if x.common_reward_type == RewardType.Item: + user_item = await self.data.item.get_user_item_by_id(x.user_common_reward_id) + static_item = await self.data.static.get_item_id(user_item['item_id']) + + if int(static_item['ItemTypeId']) == 7: + equipment_exp += int(static_item['Value']) + self.logger.info(f"Remove item {x.user_common_reward_id} and add {int(static_item['Value'])} XP (running {equipment_exp})") + await self.data.item.remove_item(x.user_common_reward_id) - if itemList: - equipment_exp = 2000 + int(synthesize_equipment_data["enhancement_exp"]) - await self.data.item.remove_item(req_data.user_id, x.user_common_reward_id) - - if equipmentList: - equipment_data = await self.data.item.get_user_equipment(req_data.user_id, x.user_common_reward_id) + elif x.common_reward_type == RewardType.Equipment: + equipment_data = await self.data.item.get_user_equipment_by_id(x.user_common_reward_id) if equipment_data is None: self.logger.error(f"Failed to find equipment {x.user_common_reward_id} for user {req_data.user_id}!") continue + + req_exp = 0 + for e in range(len(equip_level_data)): + if equipment_data['enhancement_value'] == int(equip_level_data[e]['EquipmentLevelId']): + req_exp = equip_level_data[e + 1]['RequireExp'] + break + + static_equip_data = await self.data.static.get_equipment_by_id(equipment_data['equipment_id']) + + equipment_exp += int(static_equip_data['CompositionExp']) + req_exp + self.logger.info(f"Remove equipment {x.user_common_reward_id} and add {int(static_equip_data['CompositionExp']) + req_exp} XP (running {equipment_exp})") + await self.data.item.remove_equipment(x.user_common_reward_id) - equipment_exp = int(equipment_data["enhancement_exp"]) + int(synthesize_equipment_data["enhancement_exp"]) - await self.data.item.remove_equipment(req_data.user_id, x.user_common_reward_id) - - if heroList: - hero_data = await self.data.item.get_hero_log(req_data.user_id, x.user_common_reward_id) + elif x.common_reward_type == RewardType.HeroLog: + hero_data = await self.data.item.get_hero_log_by_id(x.user_common_reward_id) if hero_data is None: self.logger.error(f"Failed to find hero {x.user_common_reward_id} for user {req_data.user_id}!") continue - equipment_exp = int(hero_data["log_exp"]) + int(synthesize_equipment_data["enhancement_exp"]) - await self.data.item.remove_hero_log(req_data.user_id, x.user_common_reward_id) - - if equipment_exp == 0: - self.logger.warn(f"Common reward {x.user_common_reward_id} (type {x.common_reward_type}) not found!") - continue + req_exp = 0 + for e in range(len(hero_level_data)): + if hero_data['log_level'] == int(hero_level_data[e]['HeroLogLevelId']): + req_exp = hero_level_data[e + 1]['RequireExp'] + break + + static_hero_data = await self.data.static.get_hero_by_id(hero_data['hero_log_id']) + + equipment_exp += int(static_hero_data['CompositionExp']) + req_exp + self.logger.info(f"Remove hero {x.user_common_reward_id} and add {int(static_hero_data['CompositionExp']) + req_exp} XP (running {equipment_exp})") + await self.data.item.remove_hero_log(x.user_common_reward_id) - await self.data.item.put_equipment_data(req_data.user_id, int(req_data.origin_user_equipment_id), synthesize_equipment_data["enhancement_value"], equipment_exp, 0, 0, 0) - - profile = await self.data.profile.get_profile(req_data.user_id) - new_col = int(profile["own_col"]) - 100 - - # Update profile - - await self.data.profile.put_profile( - req_data.user_id, - profile["user_type"], - profile["nick_name"], - profile["rank_num"], - profile["rank_exp"], - new_col, - profile["own_vp"], - profile["own_yui_medal"], - profile["setting_title_id"] - ) + else: + self.logger.warn(f"Unhandled ype {x.common_reward_type}! (running {equipment_exp})") + + equipment_exp = int(equipment_exp * 1.5) + await self.data.item.add_equipment_enhancement_exp(req_data.origin_user_equipment_id, equipment_exp) + synthesize_equipment_data = await self.data.item.get_user_equipment(req_data.user_id, req_data.origin_user_equipment_id) + equip_exp_new = synthesize_equipment_data['enhancement_exp'] + pre_synth_level = synthesize_equipment_data['enhancement_value'] + new_synth_level = 1 + + for e in range(len(equip_level_data)): + if equip_exp_new>=int(equip_level_data[e]["TotalExp"]) and equip_exp_new= 100: + col_cost = col_cost * 10 + + col_cost = col_cost * 1000 + col_cost = max(100, int(col_cost / 1000)) + + self.logger.info(f"Synthesize {equipment_exp} exp for equipment {req_data.origin_user_equipment_id}, costing {col_cost} col") + + await self.data.profile.add_col(req_data.user_id, -col_cost) + if synthesize_equipment_data is not None: + resp.after_equipment_user_data.append(EquipmentUserData.from_args(synthesize_equipment_data)) + resp.after_equipment_user_data[0].enhancement_exp = remain_exp + resp.after_equipment_user_data[0].enhancement_value = new_synth_level return resp.make() - async def handle_c806(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c806(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #custom/change_party req_data = SaoChangePartyRequest(header, request) - party_hero_list = [] - for party_team in req_data.party_data_list[0].party_team_data_list: - hero_data = await self.data.item.get_hero_log(req_data.user_id, party_team.user_hero_log_id) - hero_level = 1 - hero_exp = 0 + for party_team in req_data.party_data_list: + for hero in party_team.party_team_data_list: + await self.data.item.set_user_hero_weapons( + int(hero.user_hero_log_id), + int(hero.main_weapon_user_equipment_id) if hero.main_weapon_user_equipment_id and int(hero.main_weapon_user_equipment_id) > 0 else None, + int(hero.sub_equipment_user_equipment_id) if hero.sub_equipment_user_equipment_id and int(hero.sub_equipment_user_equipment_id) > 0 else None + ) + await self.data.item.set_user_hero_skills( + int(hero.user_hero_log_id), + hero.skill_slot1_skill_id if hero.skill_slot1_skill_id > 0 else None, + hero.skill_slot2_skill_id if hero.skill_slot2_skill_id > 0 else None, + hero.skill_slot3_skill_id if hero.skill_slot3_skill_id > 0 else None, + hero.skill_slot4_skill_id if hero.skill_slot4_skill_id > 0 else None, + hero.skill_slot5_skill_id if hero.skill_slot5_skill_id > 0 else None + ) - if hero_data: - hero_level = hero_data["log_level"] - hero_exp = hero_data["log_exp"] - await self.data.item.put_hero_log( + await self.data.item.put_hero_party( req_data.user_id, - party_team.user_hero_log_id, - hero_level, - hero_exp, - party_team.main_weapon_user_equipment_id, - party_team.sub_equipment_user_equipment_id, - party_team.skill_slot1_skill_id, - party_team.skill_slot2_skill_id, - party_team.skill_slot3_skill_id, - party_team.skill_slot4_skill_id, - party_team.skill_slot5_skill_id + party_team.team_no, + party_team.party_team_data_list[0].user_hero_log_id, + party_team.party_team_data_list[1].user_hero_log_id, + party_team.party_team_data_list[2].user_hero_log_id, ) - party_hero_list.append(party_team.user_hero_log_id) - - await self.data.item.put_hero_party(req_data.user_id, req_data.party_data_list[0].party_team_data_list[0].user_party_team_id, party_hero_list[0], party_hero_list[1], party_hero_list[2]) - - resp = SaoNoopResponse(header.cmd +1) + resp = SaoNoopResponse(GameconnectCmd.CHANGE_PARTY_RESPONSE) return resp.make() - async def handle_c904(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c900(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #quest/get_quest_scene_user_data_list + req = SaoGenericUserRequest(header, request) + + quest_data = await self.data.item.get_quest_logs(req.user_id) + + resp = SaoGetQuestSceneUserDataListResponse() + for quest in quest_data: + ex_bonus_data = await self.data.item.get_player_ex_bonus_by_quest(req.user_id, quest["quest_scene_id"]) + tmp = QuestSceneUserData.from_args(quest) + + for ex_bonus in ex_bonus_data: + tmp.quest_scene_ex_bonus_user_data_list.append(QuestSceneExBonusUserData.from_args(ex_bonus['ex_bonus_table_id'], ex_bonus['quest_clear_flag'])) + + resp.quest_scene_user_data_list.append(tmp) + return resp.make() + + async def handle_c902(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: # for whatever reason, having all entries empty or filled changes nothing + #quest/get_quest_scene_prev_scan_profile_card + resp = SaoGetQuestScenePrevScanProfileCardResponse() + resp.profile_card_data.append(ReadProfileCardData.from_args({}, {})) + return resp.make() + + async def handle_c904(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #quest/episode_play_start req_data = SaoEpisodePlayStartRequest(header, request) user_id = req_data.user_id profile_data = await self.data.profile.get_profile(user_id) - await self.data.item.create_session( + sesh_id = await self.data.item.create_session( user_id, int(req_data.play_start_request_data[0].user_party_id), req_data.episode_id, @@ -450,178 +1175,228 @@ class SaoBase: req_data.play_start_request_data[0].quest_drop_boost_apply_flag ) - resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data) + resp = SaoEpisodePlayStartResponse() + resp.play_start_response_data.append(QuestScenePlayStartResponseData.from_args(sesh_id, profile_data['nick_name'])) + resp.multi_play_start_response_data.append(QuestSceneMultiPlayStartResponseData.from_args()) return resp.make() - async def handle_c908(self, header: SaoRequestHeader, request: bytes) -> bytes: # Level calculation missing for the profile and heroes + async def handle_c908(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: # Level calculation missing for the profile and heroes #quest/episode_play_end - req_data = SaoEpisodePlayEndRequest(header, request) + resp = SaoEpisodePlayEndResponse() - # Add stage progression to database user_id = req_data.user_id episode_id = req_data.episode_id - quest_clear_flag = bool(req_data.play_end_request_data_list[0].score_data_list[0].boss_destroying_num) - clear_time = req_data.play_end_request_data_list[0].score_data_list[0].clear_time - combo_num = req_data.play_end_request_data_list[0].score_data_list[0].combo_num - total_damage = req_data.play_end_request_data_list[0].score_data_list[0].total_damage - concurrent_destroying_num = req_data.play_end_request_data_list[0].score_data_list[0].concurrent_destroying_num + play_end = req_data.play_end_request_data[0] + base_get_data = play_end.base_get_data[0] + score_data = play_end.score_data[0] + exp = 0 + cleared_mission_ct = 0 + highest_mission_diff_cleared = 0 + num_monsters_defeated = 0 + monsters_defeated_data = {} + json_data = {"data": []} + + ep_data = await self.data.static.get_episode_by_id(req_data.episode_id) + quest_scene = await self.data.static.get_quest_by_id(int(ep_data['QuestSceneId'])) + reward_table = await self.data.static.get_rewards_by_subtable(int(quest_scene['RewardTableSubId'])) + ex_bonus_table = await self.data.static.get_ex_bonuses_by_subtable(int(ep_data['ExBonusTableSubId'])) - profile = await self.data.profile.get_profile(user_id) - vp = int(profile["own_vp"]) - exp = int(profile["rank_exp"]) + 100 #always 100 extra exp for some reason - col = int(profile["own_col"]) + int(req_data.play_end_request_data_list[0].base_get_data_list[0].get_col) + await self.data.profile.add_col(user_id, base_get_data.get_col) + quest_clear_flag = score_data.clear_time > 0 + clear_time = score_data.clear_time + combo_num = score_data.combo_num + total_damage = score_data.total_damage + concurrent_destroying_num = score_data.concurrent_destroying_num + if quest_clear_flag is True: - # Save stage progression - to be revised to avoid saving worse score - - # Reference Episode.csv but Chapter 2,3,4 and 5 reports id -1, match using /10 + last digits - if episode_id > 10000 and episode_id < 11000: - # Starts at 1001 - episode_id = episode_id - 9000 - elif episode_id > 20000: - # Starts at 2001 - stage_id = str(episode_id)[-2:] - episode_id = episode_id / 10 - episode_id = int(episode_id) + int(stage_id) - - # Match episode_id with the questSceneId saved in the DB through sortNo - questId = await self.data.static.get_quests_id(episode_id) - episode_id = questId[2] - - await self.data.item.put_player_quest(user_id, episode_id, quest_clear_flag, clear_time, combo_num, total_damage, concurrent_destroying_num) - - vp = int(profile["own_vp"]) + 10 #always 10 VP per cleared stage - - + await self.data.profile.add_vp(user_id, quest_scene['SingleRewardVp']) + await self.data.profile.add_exp(user_id, quest_scene['SuccessPlayerExp']) + exp = await self.data.profile.get_exp(user_id) + + else: + await self.data.profile.add_exp(user_id, quest_scene['FailedPlayerExp']) + exp = await self.data.profile.get_exp(user_id) + # Calculate level based off experience and the CSV list - with open(r'titles/sao/data/PlayerRank.csv') as csv_file: - csv_reader = csv.reader(csv_file, delimiter=',') - line_count = 0 - data = [] - rowf = False - for row in csv_reader: - if rowf==False: - rowf=True - else: - data.append(row) + player_level_data = self.load_data_csv("PlayerRank") - for i in range(0,len(data)): - if exp>=int(data[i][1]) and exp=int(player_level_data[i]["TotalExp"]) and exp highest_mission_diff_cleared: + highest_mission_diff_cleared = mission.mission_difficulty_id + + for monster_data in play_end.discovery_enemy_data_list: + num_monsters_defeated += monster_data.destroy_num + monsters_defeated_data[monster_data.enemy_kind_id] = monster_data.destroy_num + + for bonus in ex_bonus_table: + table_id = int(bonus['ExBonusTableId']) + ach_thing = 0 + condition = int(bonus['ExBonusConditionId']) + val1 = int(bonus['ConditionValue1']) + val2 = int(bonus['ConditionValue2']) + if condition == ExBonusCondition.CLEAR_UNDER_X_SECS: + if quest_clear_flag and int(score_data.clear_time / 1000) < val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.DEFEAT_X_MONSTER_Y_TIMES: + if monsters_defeated_data.get(val1, 0) >= val2: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.DEFEAT_X_MONSTERS: + if num_monsters_defeated >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.CLEAR_X_MISSIONS: + if cleared_mission_ct >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.CLEAR_MISSION_DIFFICULTY_X: + if highest_mission_diff_cleared >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.COLLECT_X_LOGS: + if len(play_end.get_unanalyzed_log_tmp_reward_data_list) >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.CLEAR_SKILL_LEVEL_X: + if score_data.reaching_skill_level >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.NO_LOSSES: + if score_data.total_loss_num == 0: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.ACCEL_X_TIMES: + if score_data.acceleration_invocation_num >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.MAX_COMBO_X: + if score_data.combo_num >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.MULTIPLAYER_CLEAR_X: + # TODO + pass + else: + self.logger.warn(f"Unhandled EX Bonus condition {condition}") + + resp.play_end_response_data[0].ex_bonus_data_list.append(QuestScenePlayEndExBonusData.from_args(table_id, ach_thing)) + + self.logger.info(f"User {user_id} {'cleared' if quest_clear_flag else 'ended'} episode {episode_id}") + + for rare_drop in play_end.get_rare_drop_data_list: + rewardList = self.load_data_csv("QuestRareDrop") + for drop in rewardList: + if int(drop['QuestRareDropId']) == rare_drop.quest_rare_drop_id: + await self.add_reward(drop, user_id) + break + + for unanalyzed_log in play_end.get_unanalyzed_log_tmp_reward_data_list: + able_rewards: List[Row] = [] + for reward in reward_table: + if int(reward['UnanalyzedLogGradeId']) == unanalyzed_log.unanalyzed_log_grade_id: + able_rewards.append(reward) + randomized_unanalyzed_id = choice(able_rewards) + + await self.add_reward(randomized_unanalyzed_id._asdict(), user_id) + json_data["data"].append(randomized_unanalyzed_id._asdict()) + + + trace_table = await self.data.static.get_player_trace_by_subid(quest_scene['PlayerTraceTableSubId']) + + for trace in play_end.get_player_trace_data_list: + self.logger.info(f"User {user_id} obtained trace {trace.user_quest_scene_player_trace_id}") + resp.play_end_response_data[0].play_end_player_trace_reward_data_list.append(QuestScenePlayEndPlayerTraceRewardData.from_args(choice(trace_table)._asdict())) + + await self.data.item.create_end_session(user_id, ep_data['QuestSceneId'], play_end.play_result_flag, json_data["data"]) # Update heroes from the used party play_session = await self.data.item.get_session(user_id) - session_party = await self.data.item.get_hero_party(user_id, play_session["user_party_team_id"]) + session_party = await self.data.item.get_hero_party_by_id(play_session["user_party_team_id"]) + if session_party: + hero_level_data = self.load_data_csv("HeroLogLevel") + hero_list = [] + hero_list.append(session_party["user_hero_log_id_1"]) + hero_list.append(session_party["user_hero_log_id_2"]) + hero_list.append(session_party["user_hero_log_id_3"]) - hero_list = [] - hero_list.append(session_party["user_hero_log_id_1"]) - hero_list.append(session_party["user_hero_log_id_2"]) - hero_list.append(session_party["user_hero_log_id_3"]) + for i in range(0,3): + await self.data.item.add_hero_xp(hero_list[i], base_get_data.get_hero_log_exp) + log_exp = await self.data.item.get_hero_xp(hero_list[i]) - for i in range(0,len(hero_list)): - hero_data = await self.data.item.get_hero_log(user_id, hero_list[i]) + # Calculate hero level based off experience and the CSV list + if log_exp: + for e in range(0,len(hero_level_data)): + if log_exp>=int(hero_level_data[e]["TotalExp"]) and log_exp=int(data[e][1]) and log_exp bytes: + async def handle_c90a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #quest/episode_play_end_unanalyzed_log_fixed + req = SaoEpisodePlayEndUnanalyzedLogFixedRequest(header, request) + resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse() + + end_session_data = await self.data.item.get_end_session(req.user_id) + for data in end_session_data['reward_data']: + resp.play_end_unanalyzed_log_reward_data_list.append(QuestScenePlayEndUnanalyzedLogRewardData.from_args(data['UnanalyzedLogGradeId'], data)) + + return resp.make() + + async def handle_c914(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #quest/trial_tower_play_start req_data = SaoTrialTowerPlayStartRequest(header, request) user_id = req_data.user_id - floor_id = req_data.trial_tower_id profile_data = await self.data.profile.get_profile(user_id) - await self.data.item.create_session( + sesh_id = await self.data.item.create_session( user_id, int(req_data.play_start_request_data[0].user_party_id), req_data.trial_tower_id, @@ -629,374 +1404,1222 @@ class SaoBase: req_data.play_start_request_data[0].quest_drop_boost_apply_flag ) - resp = SaoEpisodePlayStartResponse(header.cmd +1, profile_data) + self.logger.info(f"User {req_data.user_id} started trial tower on floor {req_data.trial_tower_id}") + resp = SaoTrialTowerPlayStartResponse(sesh_id, profile_data['nick_name']) return resp.make() - async def handle_c918(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c918(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #quest/trial_tower_play_end req_data = SaoTrialTowerPlayEndRequest(header, request) + resp = SaoTrialTowerPlayEndResponse() - # Add tower progression to database user_id = req_data.user_id trial_tower_id = req_data.trial_tower_id - next_tower_id = 0 - quest_clear_flag = bool(req_data.play_end_request_data_list[0].score_data_list[0].boss_destroying_num) - clear_time = req_data.play_end_request_data_list[0].score_data_list[0].clear_time - combo_num = req_data.play_end_request_data_list[0].score_data_list[0].combo_num - total_damage = req_data.play_end_request_data_list[0].score_data_list[0].total_damage - concurrent_destroying_num = req_data.play_end_request_data_list[0].score_data_list[0].concurrent_destroying_num - - if quest_clear_flag is True: - # Save tower progression - to be revised to avoid saving worse score - if trial_tower_id == 9: - next_tower_id = 10001 - elif trial_tower_id == 10: - trial_tower_id = 10001 - next_tower_id = 3011 - elif trial_tower_id == 19: - next_tower_id = 10002 - elif trial_tower_id == 20: - trial_tower_id = 10002 - next_tower_id = 3021 - elif trial_tower_id == 29: - next_tower_id = 10003 - elif trial_tower_id == 30: - trial_tower_id = 10003 - next_tower_id = 3031 - elif trial_tower_id == 39: - next_tower_id = 10004 - elif trial_tower_id == 40: - trial_tower_id = 10004 - next_tower_id = 3041 - elif trial_tower_id == 49: - next_tower_id = 10005 - elif trial_tower_id == 50: - trial_tower_id = 10005 - next_tower_id = 3051 - else: - trial_tower_id = trial_tower_id + 3000 - next_tower_id = trial_tower_id + 1 - - await self.data.item.put_player_quest(user_id, trial_tower_id, quest_clear_flag, clear_time, combo_num, total_damage, concurrent_destroying_num) - - # Check if next stage is already done - checkQuest = await self.data.item.get_quest_log(user_id, next_tower_id) - if not checkQuest: - if next_tower_id != 3101: - await self.data.item.put_player_quest(user_id, next_tower_id, 0, 0, 0, 0, 0) - - # Update the profile - profile = await self.data.profile.get_profile(user_id) + play_end = req_data.play_end_request_data[0] + base_get_data = play_end.base_get_data[0] + score_data = play_end.score_data[0] + exp = 0 + cleared_mission_ct = 0 + highest_mission_diff_cleared = 0 + num_monsters_defeated = 0 + monsters_defeated_data = {} + json_data = {"data": []} - exp = int(profile["rank_exp"]) + 100 #always 100 extra exp for some reason - col = int(profile["own_col"]) + int(req_data.play_end_request_data_list[0].base_get_data_list[0].get_col) + ep_data = await self.data.static.get_tower_by_id(trial_tower_id) + quest_scene = await self.data.static.get_quest_by_id(int(ep_data['QuestSceneId'])) + reward_table = await self.data.static.get_rewards_by_subtable(int(quest_scene['RewardTableSubId'])) + ex_bonus_table = await self.data.static.get_ex_bonuses_by_subtable(int(ep_data['ExBonusTableSubId'])) + await self.data.profile.add_col(user_id, base_get_data.get_col) + + quest_clear_flag = score_data.clear_time > 0 + clear_time = score_data.clear_time + combo_num = score_data.combo_num + total_damage = score_data.total_damage + concurrent_destroying_num = score_data.concurrent_destroying_num + + if quest_clear_flag is True: + await self.data.profile.add_vp(user_id, quest_scene['SingleRewardVp']) + await self.data.profile.add_exp(user_id, quest_scene['SuccessPlayerExp']) + exp = await self.data.profile.get_exp(user_id) + + else: + await self.data.profile.add_exp(user_id, quest_scene['FailedPlayerExp']) + exp = await self.data.profile.get_exp(user_id) + # Calculate level based off experience and the CSV list - with open(r'titles/sao/data/PlayerRank.csv') as csv_file: - csv_reader = csv.reader(csv_file, delimiter=',') - line_count = 0 - data = [] - rowf = False - for row in csv_reader: - if rowf==False: - rowf=True - else: - data.append(row) + player_level_data = self.load_data_csv("PlayerRank") - for i in range(0,len(data)): - if exp>=int(data[i][1]) and exp=int(player_level_data[i]["TotalExp"]) and exp highest_mission_diff_cleared: + highest_mission_diff_cleared = mission.mission_difficulty_id + + for monster_data in play_end.discovery_enemy_data_list: + num_monsters_defeated += monster_data.destroy_num + monsters_defeated_data[monster_data.enemy_kind_id] = monster_data.destroy_num + + for bonus in ex_bonus_table: + table_id = int(bonus['ExBonusTableId']) + ach_thing = 0 + condition = int(bonus['ExBonusConditionId']) + val1 = int(bonus['ConditionValue1']) + val2 = int(bonus['ConditionValue2']) + if condition == ExBonusCondition.CLEAR_UNDER_X_SECS: + if quest_clear_flag and int(score_data.clear_time / 1000) < val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.DEFEAT_X_MONSTER_Y_TIMES: + if monsters_defeated_data.get(val1, 0) >= val2: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.DEFEAT_X_MONSTERS: + if num_monsters_defeated >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.CLEAR_X_MISSIONS: + if cleared_mission_ct >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.CLEAR_MISSION_DIFFICULTY_X: + if highest_mission_diff_cleared >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.COLLECT_X_LOGS: + if len(play_end.get_unanalyzed_log_tmp_reward_data_list) >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.CLEAR_SKILL_LEVEL_X: + if score_data.reaching_skill_level >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.NO_LOSSES: + if score_data.total_loss_num == 0: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.ACCEL_X_TIMES: + if score_data.acceleration_invocation_num >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.MAX_COMBO_X: + if score_data.combo_num >= val1: + await self.add_reward(bonus._asdict(), user_id) + await self.data.item.put_ex_bonus(user_id, int(ep_data['QuestSceneId']), table_id, True) + ach_thing = 2 + elif condition == ExBonusCondition.MULTIPLAYER_CLEAR_X: + # TODO + pass + else: + self.logger.warn(f"Unhandled EX Bonus condition {condition}") + + resp.play_end_response_data[0].ex_bonus_data_list.append(QuestScenePlayEndExBonusData.from_args(table_id, ach_thing)) + + self.logger.info(f"User {user_id} {'cleared' if quest_clear_flag else 'ended'} trial tower {trial_tower_id}") + + for rare_drop in play_end.get_rare_drop_data_list: + rewardList = self.load_data_csv("QuestRareDrop") + for drop in rewardList: + if int(drop['QuestRareDropId']) == rare_drop.quest_rare_drop_id: + await self.add_reward(drop, user_id) + break + + for unanalyzed_log in play_end.get_unanalyzed_log_tmp_reward_data_list: + able_rewards: List[Row] = [] + for reward in reward_table: + if int(reward['UnanalyzedLogGradeId']) == unanalyzed_log.unanalyzed_log_grade_id: + able_rewards.append(reward) + randomized_unanalyzed_id = choice(able_rewards) + + await self.add_reward(randomized_unanalyzed_id._asdict(), user_id) + json_data["data"].append(randomized_unanalyzed_id._asdict()) + + + trace_table = await self.data.static.get_player_trace_by_subid(quest_scene['PlayerTraceTableSubId']) + + for trace in play_end.get_player_trace_data_list: + self.logger.info(f"User {user_id} obtained trace {trace.user_quest_scene_player_trace_id}") + resp.play_end_response_data[0].play_end_player_trace_reward_data_list.append(QuestScenePlayEndPlayerTraceRewardData.from_args(choice(trace_table)._asdict())) + + await self.data.item.create_end_session(user_id, ep_data['QuestSceneId'], play_end.play_result_flag, json_data["data"]) # Update heroes from the used party play_session = await self.data.item.get_session(user_id) - session_party = await self.data.item.get_hero_party(user_id, play_session["user_party_team_id"]) + session_party = await self.data.item.get_hero_party_by_id(play_session["user_party_team_id"]) + if session_party: + hero_level_data = self.load_data_csv("HeroLogLevel") + hero_list = [] + hero_list.append(session_party["user_hero_log_id_1"]) + hero_list.append(session_party["user_hero_log_id_2"]) + hero_list.append(session_party["user_hero_log_id_3"]) - hero_list = [] - hero_list.append(session_party["user_hero_log_id_1"]) - hero_list.append(session_party["user_hero_log_id_2"]) - hero_list.append(session_party["user_hero_log_id_3"]) + for i in range(0,3): + self.logger.info(f"Give hero {hero_list[i]} {base_get_data.get_hero_log_exp}") + await self.data.item.add_hero_xp(hero_list[i], base_get_data.get_hero_log_exp) + log_exp = await self.data.item.get_hero_xp(hero_list[i]) - for i in range(0,len(hero_list)): - hero_data = await self.data.item.get_hero_log(user_id, hero_list[i]) + # Calculate hero level based off experience and the CSV list + if log_exp: + for e in range(0,len(hero_level_data)): + if log_exp>=int(hero_level_data[e]["TotalExp"]) and log_exp=int(data[e][1]) and log_exp bytes: - #quest/episode_play_end_unanalyzed_log_fixed - - req = SaoEpisodePlayEndUnanalyzedLogFixedRequest(header, request) - - end_session_data = await self.data.item.get_end_session(req.user_id) - - resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4]) - return resp.make() - - async def handle_c91a(self, header: SaoRequestHeader, request: bytes) -> bytes: # handler is identical to the episode + async def handle_c91a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: # handler is identical to the episode #quest/trial_tower_play_end_unanalyzed_log_fixed - req = TrialTowerPlayEndUnanalyzedLogFixed(header, request) - + req = SaoTrialTowerPlayEndUnanalyzedLogFixedRequest(header, request) + resp = SaoTrialTowerPlayEndUnanalyzedLogFixedResponse() + end_session_data = await self.data.item.get_end_session(req.user_id) + for data in end_session_data['reward_data']: + resp.play_end_unanalyzed_log_reward_data_list.append(QuestScenePlayEndUnanalyzedLogRewardData.from_args(data['UnanalyzedLogGradeId'], data)) - resp = SaoEpisodePlayEndUnanalyzedLogFixedResponse(header.cmd +1, end_session_data[4]) return resp.make() - async def handle_cd00(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_c930(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # quest/get_chat_side_story_user_data_list + req = SaoGenericUserRequest(header, request) + resp = SaoGetChatSideStoryUserDataListResponse() + + return resp.make() + + async def handle_ca02(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + #quest_multi_play_room/get_quest_scene_multi_play_photon_server + resp = SaoGetQuestSceneMultiPlayPhotonServerResponse(self.game_cfg.server.photon_app_id) + return resp.make() + + async def handle_cb02(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # quest_ranking/get_quest_hierarchy_progress_degrees_ranking_list + req = SaoGetQuestHierarchyProgressDegreesRankingListRequest(header, request) + return SaoGetQuestHierarchyProgressDegreesRankingListResponse(GameconnectCmd.GET_QUEST_HIERARCHY_PROGRESS_DEGREES_RANKING_LIST_RESPONSE).make() + + async def handle_cb04(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # quest_ranking/get_quest_popular_hero_log_ranking_list + req = SaoGetQuestPopularHeroLogRankingListRequest(header, request) + return SaoGetQuestPopularHeroLogRankingListResponse(GameconnectCmd.GET_QUEST_POPULAR_HERO_LOG_RANKING_LIST_RESPONSE).make() + + async def handle_cd00(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #defrag_match/get_defrag_match_basic_data - resp = SaoGetDefragMatchBasicDataResponse(header.cmd +1) + resp = SaoGetDefragMatchBasicDataResponse() + data = DefragMatchBasicUserData.from_args() return resp.make() - async def handle_cd02(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cd02(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #defrag_match/get_defrag_match_ranking_user_data - resp = SaoGetDefragMatchRankingUserDataResponse(header.cmd +1) + # TODO: League points + req = SaoGetDefragMatchRankingUserDataRequest(header, request) + profile = await self.data.profile.get_profile(req.user_id) + resp = SaoGetDefragMatchRankingUserDataResponse(profile._asdict()) return resp.make() - async def handle_cd04(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cd04(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #defrag_match/get_defrag_match_league_point_ranking_list - resp = SaoGetDefragMatchLeaguePointRankingListResponse(header.cmd +1) + resp = SaoGetDefragMatchLeaguePointRankingListResponse() return resp.make() - async def handle_cd06(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cd06(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: #defrag_match/get_defrag_match_league_score_ranking_list - resp = SaoGetDefragMatchLeagueScoreRankingListResponse(header.cmd +1) + resp = SaoGetDefragMatchLeagueScoreRankingListResponse() return resp.make() - async def handle_d404(self, header: SaoRequestHeader, request: bytes) -> bytes: - #other/bnid_serial_code_check - resp = SaoBnidSerialCodeCheckResponse(header.cmd +1) - return resp.make() - - async def handle_c306(self, header: SaoRequestHeader, request: bytes) -> bytes: - #card/scan_qr_quest_profile_card - resp = SaoScanQrQuestProfileCardResponse(header.cmd +1) - return resp.make() - - async def handle_c700(self, header: SaoRequestHeader, request: bytes) -> bytes: - # shop/get_shop_resource_sales_data_list - # TODO: Get user shop data - req = GetShopResourceSalesDataListRequest(header, request) - resp = GetShopResourceSalesDataListResponse(header.cmd + 1) - return resp.make() - - async def handle_d100(self, header: SaoRequestHeader, request: bytes) -> bytes: - # shop/get_yui_medal_shop_user_data_list - # TODO: Get user shop data - req = GetYuiMedalShopUserDataListRequest(header, request) - resp = GetYuiMedalShopUserDataListResponse(header.cmd + 1) - return resp.make() - - async def handle_cf0e(self, header: SaoRequestHeader, request: bytes) -> bytes: + async def handle_cf0e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: # gasha/get_gasha_medal_shop_user_data_list # TODO: Get user shop data req = GetGashaMedalShopUserDataListRequest(header, request) - resp = GetGashaMedalShopUserDataListResponse(header.cmd + 1) + resp = GetGashaMedalShopUserDataListResponse(GameconnectCmd.GET_GASHA_MEDAL_SHOP_USER_DATA_LIST_RESPONSE) return resp.make() - - async def handle_d5da(self, header: SaoRequestHeader, request: bytes) -> bytes: + + async def handle_d000(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + req = SaoGenericUserRequest(header, request) + resp = SaoGetAdventureExecUserDataResponse() + return resp.make() + + async def handle_d100(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # shop/get_yui_medal_shop_user_data_list + # TODO: Get user shop data + req = GetYuiMedalShopUserDataListRequest(header, request) + resp = GetYuiMedalShopUserDataListResponse(GameconnectCmd.GET_YUI_MEDAL_SHOP_USER_DATA_LIST_RESPONSE) + return resp.make() + + async def handle_d200(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # mission/get_beginner_mission_user_data + req = SaoGetBeginnerMissionUserDataRequest(header, request) + resp = SaoGetBeginnerMissionUserDataResponse() + profile = await self.data.profile.get_profile(req.user_id) + if profile: + if profile['ad_confirm_date']: + resp.data[0].ad_confirm_date = profile['ad_confirm_date'] + resp.data[0].ad_confirm_flag = 1 + + return resp.make() + + async def handle_d202(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # mission/get_beginner_mission_progresses_user_data_list + req = SaoGetBeginnerMissionProgressesUserDataListRequest(header, request) + resp = SaoGetBeginnerMissionProgressesUserDataListResponse() + + return resp.make() + + async def handle_d204(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # mission/get_beginner_mission_seat_progresses_user_data_list + req = SaoGetBeginnerMissionSeatProgressesUserDataListRequest(header, request) + resp = SaoGetBeginnerMissionSeatProgressesUserDataListResponse() + + return resp.make() + + async def handle_d206(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # mission/beginner_mission_ad_confirm_notification + req = SaoBeginnerMissionAdConfirmNotificationRequest(header, request) + self.logger.info(f"User {req.user_id} confirmed ad for beginner mission {req.beginner_mission_id}") + await self.data.profile.update_beginner_mission_date(req.user_id) + + return SaoNoopResponse(GameconnectCmd.BEGINNER_MISSION_AD_CONFIRM_NOTIFICATION_RESPONSE).make() + + async def handle_d312(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # campaign/get_linked_site_reg_campaign_user_data + req = SaoGetLinkedSiteRegCampaignUserDataRequest(header, request) + resp = SaoGetLinkedSiteRegCampaignUserDataResponse() + + return resp.make() + + async def handle_d400(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # other/get_hero_log_unit_user_data_list + req = SaoGenericUserRequest(header, request) + resp = SaoGetHeroLogUnitUserDataListResponse() + + return resp.make() + + async def handle_d402(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # other/get_chara_unit_user_data_list + req = SaoGenericUserRequest(header, request) + resp = SaoGetCharaUnitUserDataListResponse() + + return resp.make() + + async def handle_d404(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # other/bnid_serial_code_check + req = SaoBnidSerialCodeCheckRequest() + resp = SaoBnidSerialCodeCheckResponse() + return resp.make() + + async def handle_d404(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # other/bnid_serial_code_entry_by_appendix_card + req = SaoBnidSerialCodeEntryByAppendixCardRequest() + resp = SaoBnidSerialCodeEntryByAppendixCardResponse() + return resp.make() + + async def handle_d500(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_player_ranks + tbl = self.load_data_csv('PlayerRank') + resp = SaoGetMPlayerRanksResponse(tbl) + return resp.make() + + async def handle_d502(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_titles + tbl = self.load_data_csv('Title') + resp = SaoGetMTitlesResponse(tbl) + return resp.make() + + async def handle_d504(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_fragments + tbl = self.load_data_csv('Fragment') + resp = SaoGetMFragmentsResponse(tbl) + return resp.make() + + async def handle_d506(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_reward_tables + tbl = self.load_data_csv('RewardTable') + resp = SaoGetMRewardTablesResponse(tbl) + return resp.make() + + async def handle_d508(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_reward_sets + tbl = self.load_data_csv('RewardSet') + resp = SaoGetMRewardSetsResponse(tbl) + return resp.make() + + async def handle_d50a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_unanalyzed_log_grades + tbl = self.load_data_csv('UnanalyzedLogGrade') + resp = SaoGetMUnanalyzedLogGradesResponse(tbl) + return resp.make() + + async def handle_d50c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_appoint_leader_params + tbl = self.load_data_csv('AppointLeaderParam') + resp = SaoGetMAppointLeaderParamsResponse(tbl) + return resp.make() + + async def handle_d50e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_appoint_leader_effects + tbl = self.load_data_csv('AppointLeaderEffect') + resp = SaoGetMAppointLeaderEffectsResponse(tbl) + return resp.make() + + async def handle_d510(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_appoint_leader_effect_types + tbl = self.load_data_csv('AppointLeaderEffectType') + resp = SaoGetMAppointLeaderEffectTypesResponse(tbl) + return resp.make() + + async def handle_d512(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_rarities + tbl = self.load_data_csv('Rarity') + resp = SaoGetMRaritiesResponse(tbl) + return resp.make() + + async def handle_d514(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_composition_events + tbl = self.load_data_csv('CompositionEvent') + resp = SaoGetMCompositionEventsResponse(tbl) + return resp.make() + + async def handle_d516(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_composition_params + tbl = self.load_data_csv('CompositionParam') + resp = SaoGetMCompositionParamsResponse(tbl) + return resp.make() + + async def handle_d518(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_game_play_prices + tbl = self.load_data_csv('GamePlayPrice') + resp = SaoGetMGamePlayPricesResponse(tbl) + return resp.make() + + async def handle_d51a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_buy_tickets + tbl = self.load_data_csv('BuyTicket') + resp = SaoGetMBuyTicketsResponse(tbl) + return resp.make() + + async def handle_d51c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_tips + tbl = self.load_data_csv('Tips') + resp = SaoGetMTipsResponse(tbl) + return resp.make() + + async def handle_d51e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_caps + tbl = self.load_data_csv('Cap') + resp = SaoGetMCapsResponse(tbl) + return resp.make() + + async def handle_d520(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_hero_log + tbl = self.load_data_csv('HeroLog') + resp = SaoGetMHeroLogResponse(tbl) + return resp.make() + + async def handle_d522(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_hero_log_levels + tbl = self.load_data_csv('HeroLogLevel') + resp = SaoGetMHeroLogLevelsResponse(tbl) + return resp.make() + + async def handle_d524(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_hero_log_roles + tbl = self.load_data_csv('HeroLogRole') + resp = SaoGetMHeroLogRolesResponse(tbl) + return resp.make() + + async def handle_d526(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_hero_log_trust_ranks + tbl = self.load_data_csv('HeroLogTrustRank') + resp = SaoGetMHeroLogTrustRanksResponse(tbl) + return resp.make() + + async def handle_d528(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_charas + tbl = self.load_data_csv('Chara') + resp = SaoGetMCharasResponse(tbl) + return resp.make() + + async def handle_d52a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_chara_friendly_ranks + tbl = self.load_data_csv('CharaFriendlyRank') + resp = SaoGetMCharaFriendlyRanksResponse(tbl) + return resp.make() + + async def handle_d52c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_equipments + tbl = self.load_data_csv('Equipment') + resp = SaoGetMEquipmentsResponse(tbl) + return resp.make() + + async def handle_d52e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_equipment_levels + tbl = self.load_data_csv('EquipmentLevel') + resp = SaoGetMEquipmentLevelsResponse(tbl) + return resp.make() + + async def handle_d530(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_weapon_types + tbl = self.load_data_csv('WeaponType') + resp = SaoGetMWeaponTypesResponse(tbl) + return resp.make() + + async def handle_d532(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_items + tbl = self.load_data_csv('Item') + resp = SaoGetMItemsResponse(tbl) + return resp.make() + + async def handle_d534(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_item_types + tbl = self.load_data_csv('ItemType') + resp = SaoGetMItemTypesResponse(tbl) + return resp.make() + + async def handle_d536(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_buff_items + tbl = self.load_data_csv('BuffItem') + resp = SaoGetMBuffItemsResponse(tbl) + return resp.make() + + async def handle_d538(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_enemies + tbl = self.load_data_csv('Enemy') + resp = SaoGetMEnemiesResponse(tbl) + return resp.make() + + async def handle_d53a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_enemy_sets + tbl = self.load_data_csv('EnemySet') + resp = SaoGetMEnemySetsResponse(tbl) + return resp.make() + + async def handle_d53c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_enemy_kinds + tbl = self.load_data_csv('EnemyKind') + resp = SaoGetMEnemyKindsResponse(tbl) + return resp.make() + + async def handle_d53e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_enemy_categories + tbl = self.load_data_csv('EnemyCategory') + resp = SaoGetMEnemyCategoriesResponse(tbl) + return resp.make() + + async def handle_d540(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_units + tbl = self.load_data_csv('Unit') + resp = SaoGetMUnitsResponse(tbl) + return resp.make() + + async def handle_d542(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_unit_gimmicks + tbl = self.load_data_csv('UnitGimmick') + resp = SaoGetMUnitGimmicksResponse(tbl) + return resp.make() + + async def handle_d544(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_unit_collisions + tbl = self.load_data_csv('UnitCollision') + resp = SaoGetMUnitCollisionsResponse(tbl) + return resp.make() + + async def handle_d546(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_unit_powers + tbl = self.load_data_csv('UnitPower') + resp = SaoGetMUnitPowersResponse(tbl) + return resp.make() + + async def handle_d548(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gimmick_attacks + tbl = self.load_data_csv('GimmickAttack') + resp = SaoGetMGimmickAttacksResponse(tbl) + return resp.make() + + async def handle_d54a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_chara_attacks + tbl = self.load_data_csv('CharaAttack') + resp = SaoGetMCharaAttacksResponse(tbl) + return resp.make() + + async def handle_d54c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_boss_attacks + tbl = self.load_data_csv('BossAttack') + resp = SaoGetMBossAttacksResponse(tbl) + return resp.make() + + async def handle_d54e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_monster_attacks + tbl = self.load_data_csv('MonsterAttack') + resp = SaoGetMMonsterAttacksResponse(tbl) + return resp.make() + + async def handle_d550(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_monster_actions + tbl = self.load_data_csv('MonsterAction') + resp = SaoGetMMonsterActionsResponse(tbl) + return resp.make() + + async def handle_d552(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_properties + tbl = self.load_data_csv('Property') + resp = SaoGetMPropertiesResponse(tbl) + return resp.make() + + async def handle_d554(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_property_tables + tbl = self.load_data_csv('PropertyTable') + resp = SaoGetMPropertyTablesResponse(tbl) + return resp.make() + + async def handle_d556(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_property_types + tbl = self.load_data_csv('PropertyType') + resp = SaoGetMPropertyTypesResponse(tbl) + return resp.make() + + async def handle_d558(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_skills + tbl = self.load_data_csv('Skill') + resp = SaoGetMSkillsResponse(tbl) + return resp.make() + + async def handle_d55a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_skill_tables + tbl = self.load_data_csv('SkillTable') + resp = SaoGetMSkillTablesResponse(tbl) + return resp.make() + + async def handle_d55c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_skill_levels + tbl = self.load_data_csv('SkillLevel') + resp = SaoGetMSkillLevelsResponse(tbl) + return resp.make() + + async def handle_d55e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_awakenings + tbl = self.load_data_csv('Awakening') + resp = SaoGetMAwakeningsResponse(tbl) + return resp.make() + + async def handle_d560(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_synchro_skills + tbl = self.load_data_csv('SynchroSkill') + resp = SaoGetMSynchroSkillsResponse(tbl) + return resp.make() + + async def handle_d562(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_sound_skill_cut_in_voices + tbl = self.load_data_csv('Sound_SkillCutInVoice') + resp = SaoGetMSoundSkillCutInVoicesResponse(tbl) + return resp.make() + + async def handle_d564(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_scenes + tbl = self.load_data_csv('QuestScene') + resp = SaoGetMQuestScenesResponse(tbl) + return resp.make() + + async def handle_d566(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_exist_units + tbl = self.load_data_csv('QuestExistUnit') + resp = SaoGetMQuestExistUnitsResponse(tbl) + return resp.make() + + async def handle_d568(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_episode_append_rewards + tbl = self.load_data_csv('QuestEpisodeAppendRewards') + resp = SaoGetMQuestEpisodeAppendRewardsResponse(tbl) + return resp.make() + + async def handle_d56a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_side_quests + tbl = self.load_data_csv('SideQuest') + resp = SaoGetMSideQuestsResponse(tbl) + return resp.make() + + async def handle_d56c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_episodes + tbl = self.load_data_csv('Episode') + resp = SaoGetMEpisodesResponse(tbl) + return resp.make() + + async def handle_d56e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_episode_chapters + tbl = self.load_data_csv('EpisodeChapter') + resp = SaoGetMEpisodeChaptersResponse(tbl) + return resp.make() + + async def handle_d570(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_episode_parts + tbl = self.load_data_csv('EpisodePart') + resp = SaoGetMEpisodePartsResponse(tbl) + return resp.make() + + async def handle_d572(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_trial_towers + tbl = self.load_data_csv('TrialTower') + resp = SaoGetMTrialTowersResponse(tbl) + return resp.make() + + async def handle_d574(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_ex_towers + req = SaoGetMExTowersRequest(header, request) + tbl = self.load_data_csv('ExTowers') + resp = SaoGetMExTowersResponse(tbl) + return resp.make() + + async def handle_d576(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_ex_tower_quests + req = SaoGetMExTowerQuestsRequest(header, request) + tbl = self.load_data_csv('ExTowerQuests') + resp = SaoGetMExTowerQuestsResponse(tbl) + return resp.make() + + async def handle_d578(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_menu_display_enemies + tbl = self.load_data_csv('MenuDisplayEnemy') + resp = SaoGetMMenuDisplayEnemiesResponse(tbl) + return resp.make() + + async def handle_d57a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_missions + tbl = self.load_data_csv('Mission') + resp = SaoGetMMissionsResponse(tbl) + return resp.make() + + async def handle_d57c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_mission_tables + tbl = self.load_data_csv('MissionTable') + resp = SaoGetMMissionTablesResponse(tbl) + return resp.make() + + async def handle_d57e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_mission_difficulties + tbl = self.load_data_csv('MissionDifficulty') + resp = SaoGetMMissionDifficultiesResponse(tbl) + return resp.make() + + async def handle_d580(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_battle_cameras + tbl = self.load_data_csv('BattleCamera') + resp = SaoGetMBattleCamerasResponse(tbl) + return resp.make() + + async def handle_d582(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_chat_main_stories + tbl = self.load_data_csv('ChatMainStory') + resp = SaoGetMChatMainStoriesResponse(tbl) + return resp.make() + + async def handle_d584(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_chat_side_stories + tbl = self.load_data_csv('ChatSideStory') + resp = SaoGetMChatSideStoriesResponse(tbl) + return resp.make() + + async def handle_d586(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_chat_event_stories + req = SaoGetMChatEventStoriesRequest(header, request) + tbl = self.load_data_csv('ChatEventStory') + resp = SaoGetMChatEventStoriesResponse(tbl) + return resp.make() + + async def handle_d588(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_navigator_charas + tbl = self.load_data_csv('NavigatorChara') + resp = SaoGetMNavigatorCharasResponse(tbl) + return resp.make() + + async def handle_d58a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_navigator_comments + tbl = self.load_data_csv('NavigatorComment') + resp = SaoGetMNavigatorCommentsResponse(tbl) + return resp.make() + + async def handle_d58c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_ex_bonus_tables + tbl = self.load_data_csv('ExBonusTable') + resp = SaoGetMExBonusTablesResponse(tbl) + return resp.make() + + async def handle_d58e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_ex_bonus_conditions + tbl = self.load_data_csv('ExBonusCondition') + resp = SaoGetMExBonusConditionsResponse(tbl) + return resp.make() + + async def handle_d590(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_rare_drops + tbl = self.load_data_csv('QuestRareDrop') + resp = SaoGetMQuestRareDropsResponse(tbl) + return resp.make() + + async def handle_d592(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_special_rare_drop_settings + tbl = self.load_data_csv('QuestSpecialRareDropSettings') + resp = SaoGetMQuestSpecialRareDropSettingsResponse(tbl) + return resp.make() + + async def handle_d594(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_special_rare_drops + tbl = self.load_data_csv('QuestSpecialRareDrops') + resp = SaoGetMQuestSpecialRareDropsResponse(tbl) + return resp.make() + + async def handle_d596(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_tutorials + tbl = self.load_data_csv('QuestTutorial') + resp = SaoGetMQuestTutorialsResponse(tbl) + return resp.make() + + async def handle_d598(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_player_trace_tables + tbl = self.load_data_csv('PlayerTraceTable') + resp = SaoGetMQuestPlayerTraceTablesResponse(tbl) + return resp.make() + + async def handle_d59a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_stills + tbl = self.load_data_csv('QuestStill') + resp = SaoGetMQuestStillsResponse(tbl) + return resp.make() + + async def handle_d59c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gashas + tbl = self.load_data_csv('Gasha') + resp = SaoGetMGashasResponse(tbl) + return resp.make() + + async def handle_d59e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gasha_headers + tbl = self.load_data_csv('GashaHeader') + resp = SaoGetMGashaHeadersResponse(tbl) + return resp.make() + + async def handle_d5a0(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gasha_lottery_rarities + tbl = self.load_data_csv('GashaLotteryRarity') + resp = SaoGetMGashaLotteryRaritiesResponse(tbl) + return resp.make() + + async def handle_d5a2(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gasha_prizes + tbl = self.load_data_csv('GashaPrize') + resp = SaoGetMGashaPrizesResponse(tbl) + return resp.make() + + async def handle_d5a4(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_comeback_events + tbl = self.load_data_csv('ComebackEvent') + resp = SaoGetMComebackEventsResponse(tbl) + return resp.make() + + async def handle_d5a6(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_ad_banners + tbl = self.load_data_csv('AdBanners') + resp = SaoGetMAdBannersResponse(tbl) + return resp.make() + + async def handle_d5a8(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_events + tbl = self.load_data_csv('Event') + resp = SaoGetMEventsResponse(tbl) + return resp.make() + + async def handle_d5aa(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_treasure_hunts + req = SaoGetMTreasureHuntsRequest(header, request) + tbl = self.load_data_csv('TreasureHunt') + resp = SaoGetMTreasureHuntsResponse(tbl) + return resp.make() + + async def handle_d5ac(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_treasure_hunt_whole_tasks + req = SaoGetMTreasureHuntWholeTasksRequest(header, request) + tbl = self.load_data_csv('TreasureHuntWholeTask') + resp = SaoGetMTreasureHuntWholeTasksResponse(tbl) + return resp.make() + + async def handle_d5ae(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_treasure_hunt_individual_tasks + req = SaoGetMTreasureHuntIndividualTasksRequest(header, request) + tbl = self.load_data_csv('TreasureHuntIndividualTask') + resp = SaoGetMTreasureHuntIndividualTasksResponse(tbl) + return resp.make() + + async def handle_d5b0(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_treasure_hunt_special_effects + req = SaoGetMTreasureHuntSpecialEffectsRequest(header, request) + tbl = self.load_data_csv('TreasureHuntSpecialEffect') + resp = SaoGetMTreasureHuntSpecialEffectsResponse(tbl) + return resp.make() + + async def handle_d5b2(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_treasure_hunt_event_point_reward_common_rewards + req = SaoGetMTreasureHuntEventPointRewardCommonRewardsRequest(header, request) + tbl = self.load_data_csv('TreasureHuntEventPointRewardCommonReward') + resp = SaoGetMTreasureHuntEventPointRewardCommonRewardsResponse(tbl) + return resp.make() + + async def handle_d5b4(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_treasure_hunt_event_point_reward_titles + req = SaoGetMTreasureHuntEventPointRewardTitlesRequest(header, request) + tbl = self.load_data_csv('TreasureHuntEventPointRewardTitle') + resp = SaoGetMTreasureHuntEventPointRewardTitlesResponse(tbl) + return resp.make() + + async def handle_d5b6(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_treasure_hunt_task_texts + tbl = self.load_data_csv('TreasureHuntTaskText') + resp = SaoGetMTreasureHuntTaskTextsResponse(tbl) + return resp.make() + + async def handle_d5b8(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_bnid_serial_codes + tbl = self.load_data_csv('BnidSerialCodes') + resp = SaoGetMBnidSerialCodesResponse(tbl) + return resp.make() + + async def handle_d5ba(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_bnid_serial_code_rewards + tbl = self.load_data_csv('BnidSerialCodeRewards') + resp = SaoGetMBnidSerialCodeRewardsResponse(tbl) + return resp.make() + + async def handle_d5bc(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_support_log + tbl = self.load_data_csv('SupportLog') + resp = SaoGetMSupportLogResponse(tbl) + return resp.make() + + async def handle_d5be(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_support_log_types + tbl = self.load_data_csv('SupportLogType') + resp = SaoGetMSupportLogTypesResponse(tbl) + return resp.make() + + async def handle_d5c0(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_episode_appends + tbl = self.load_data_csv('EpisodeAppends') + resp = SaoGetMEpisodeAppendsResponse(tbl) + return resp.make() + + async def handle_d5c2(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_defrag_match_quests + tbl = self.load_data_csv('DefragMatchQuest') + resp = SaoGetMQuestDefragMatchQuestsResponse(tbl) + return resp.make() + + async def handle_d5c4(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_quest_defrag_match_quest_boss_tables + tbl = self.load_data_csv('DefragMatchBossTable') + resp = SaoGetMQuestDefragMatchQuestBossTablesResponse(tbl) + return resp.make() + + async def handle_d5c6(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_matches + req = SaoGetMDefragMatchesRequest(header, request) + tbl = self.load_data_csv('DefragMatchs') + resp = SaoGetMDefragMatchesResponse(tbl) + return resp.make() + + async def handle_d5c8(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_seed + req = SaoGetMDefragMatchSeedRequest(header, request) + tbl = self.load_data_csv('DefragMatchSeed') + resp = SaoGetMDefragMatchSeedResponse(tbl) + return resp.make() + + async def handle_d5ca(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_special_effects + req = SaoGetMDefragMatchSpecialEffectsRequest(header, request) + tbl = self.load_data_csv('DefragMatchSpecialEffects') + resp = SaoGetMDefragMatchSpecialEffectsResponse(tbl) + return resp.make() + + async def handle_d5cc(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_grades + req = SaoGetMDefragMatchGradesRequest(header, request) + tbl = self.load_data_csv('DefragMatchGrade') + resp = SaoGetMDefragMatchGradesResponse(tbl) + return resp.make() + + async def handle_d5ce(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_cpu_units + tbl = self.load_data_csv('DefragMatchCpuUnits') + resp = SaoGetMDefragMatchCpuUnitsResponse(tbl) + return resp.make() + + async def handle_d5d0(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_cpu_support_logs + tbl = self.load_data_csv('DefragMatchCpuSupportLogs') + resp = SaoGetMDefragMatchCpuSupportLogsResponse(tbl) + return resp.make() + + async def handle_d5d2(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_period_bonuses + req = SaoGetMDefragMatchPeriodBonusesRequest(header, request) + tbl = self.load_data_csv('DefragMatchPeriodBonuses') + resp = SaoGetMDefragMatchPeriodBonusesResponse(tbl) + return resp.make() + + async def handle_d5d4(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_random_bonus_tables + req = SaoGetMDefragMatchRandomBonusTablesRequest(header, request) + tbl = self.load_data_csv('DefragMatchRandomBonusTables') + resp = SaoGetMDefragMatchRandomBonusTablesResponse(tbl) + return resp.make() + + async def handle_d5d6(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_random_bonus_conditions + tbl = self.load_data_csv('DefragMatchRandomBonusConditions') + resp = SaoGetMDefragMatchRandomBonusConditionsResponse(tbl) + return resp.make() + + async def handle_d5d8(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_defrag_match_rare_drops + req = SaoGetMDefragMatchRareDropsRequest(header, request) + tbl = self.load_data_csv('DefragMatchRareDrops') + resp = SaoGetMDefragMatchRareDropsResponse(tbl) + return resp.make() + + async def handle_d5da(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: # master_data/get_m_yui_medal_shops - req = GetMYuiMedalShopDataRequest(header, request) - resp = GetMYuiMedalShopDataResponse(header.cmd + 1) - - shops = self.load_data_csv("YuiMedalShops") - for shop in shops: - tmp = YuiMedalShopData.from_args(int(shop['YuiMedalShopId']), shop['Name'], shop['Description']) - tmp.selling_yui_medal = int(shop['SellingYuiMedal']) - tmp.selling_col = int(shop['SellingCol']) - tmp.selling_event_item_id = int(shop['SellingEventItemId']) - tmp.selling_event_item_num = int(shop['SellingEventItemNum']) - tmp.selling_ticket_num = int(shop['SellingTicketNum']) - tmp.purchase_limit = int(shop['PurchaseLimit']) - tmp.pick_up_flag = 1 if shop['PickUpFlag'] == "True" else 0 - tmp.product_category = int(shop['ProductCategory']) - tmp.sales_type = int(shop['SalesType']) - tmp.target_days = int(shop['TargetDays']) - tmp.target_hour = int(shop['TargetHour']) - tmp.interval_hour = int(shop['IntervalHour']) - tmp.sort = int(shop['Sort']) - - tmp.sales_end_date = datetime(2121, 1, 1, 0, 0, 0, 0) # always open - - resp.data_list.append(tmp) - self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shops") + tbl = self.load_data_csv('YuiMedalShops') + resp = SaoGetMYuiMedalShopsResponse(tbl) return resp.make() - - async def handle_d5dc(self, header: SaoRequestHeader, request: bytes) -> bytes: + + async def handle_d5dc(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: # master_data/get_m_yui_medal_shop_items - req = GetMYuiMedalShopItemsRequest(header, request) - resp = GetMYuiMedalShopItemsResponse(header.cmd + 1) - - shops = self.load_data_csv("YuiMedalShopItems") - for shop in shops: - tmp = YuiMedalShopItemData.from_args(int(shop['YuiMedalShopItemId']), int(shop['YuiMedalShopId']), int(shop['CommonRewardType']), int(shop['CommonRewardId']), int(shop['CommonRewardNum']), int(shop['Strength'])) - - tmp.property1_property_id = int(shop['Property1PropertyId']) - tmp.property1_value1 = int(shop['Property1Value1']) - tmp.property1_value2 = int(shop['Property1Value2']) - - tmp.property2_property_id = int(shop['Property2PropertyId']) - tmp.property2_value1 = int(shop['Property2Value1']) - tmp.property2_value2 = int(shop['Property2Value2']) - - tmp.property3_property_id = int(shop['Property3PropertyId']) - tmp.property3_value1 = int(shop['Property3Value1']) - tmp.property3_value2 = int(shop['Property3Value2']) - - tmp.property4_property_id = int(shop['Property4PropertyId']) - tmp.property4_value1 = int(shop['Property4Value1']) - tmp.property4_value2 = int(shop['Property4Value2']) - - resp.data_list.append(tmp) - - self.logger.debug(f"Load {len(resp.data_list)} Yui Medal Shop Items") + tbl = self.load_data_csv('YuiMedalShopItems') + resp = SaoGetMYuiMedalShopItemsResponse(tbl) return resp.make() - - async def handle_d5fc(self, header: SaoRequestHeader, request: bytes) -> bytes: + + async def handle_d5de(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_event_scenes + req = SaoGetMEventScenesRequest(header, request) + tbl = self.load_data_csv('EventScenes') + resp = SaoGetMEventScenesResponse(tbl) + return resp.make() + + async def handle_d5e0(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_generic_campaign_periods + tbl = self.load_data_csv('GenericCampaignPeriods') + resp = SaoGetMGenericCampaignPeriodsResponse(tbl) + return resp.make() + + async def handle_d5e2(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_beginner_missions + tbl = self.load_data_csv('BeginnerMissions') + resp = SaoGetMBeginnerMissionsResponse(tbl) + return resp.make() + + async def handle_d5e4(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_beginner_mission_conditions + req = SaoGetMBeginnerMissionConditionsRequest(header, request) + tbl = self.load_data_csv('BeginnerMissionConditions') + resp = SaoGetMBeginnerMissionConditionsResponse(tbl) + return resp.make() + + async def handle_d5e6(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_beginner_mission_rewards + req = SaoGetMBeginnerMissionRewardsRequest(header, request) + tbl = self.load_data_csv('BeginnerMissionRewards') + resp = SaoGetMBeginnerMissionRewardsResponse(tbl) + return resp.make() + + async def handle_d5e8(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_beginner_mission_seat_conditions + req = SaoGetMBeginnerMissionSeatConditionsRequest(header, request) + tbl = self.load_data_csv('BeginnerMissionSeatConditions') + resp = SaoGetMBeginnerMissionSeatConditionsResponse(tbl) + return resp.make() + + async def handle_d5ea(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_beginner_mission_seat_rewards + req = SaoGetMBeginnerMissionSeatRewardsRequest(header, request) + tbl = self.load_data_csv('BeginnerMissionSeatRewards') + resp = SaoGetMBeginnerMissionSeatRewardsResponse(tbl) + return resp.make() + + async def handle_d5ec(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_event_items + tbl = self.load_data_csv('EventItems') + resp = SaoGetMEventItemsResponse(tbl) + return resp.make() + + async def handle_d5ee(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_event_monsters + req = SaoGetMEventMonstersRequest(header, request) + tbl = self.load_data_csv('EventMonsters') + resp = SaoGetMEventMonstersResponse(tbl) + return resp.make() + + async def handle_d5f0(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_yui_medal_bonuses + tbl = self.load_data_csv('YuiMedalBonus') + resp = SaoGetMYuiMedalBonusesResponse(tbl) + return resp.make() + + async def handle_d5f2(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_yui_medal_bonus_conditions + tbl = self.load_data_csv('YuiMedalBonusCondition') + resp = SaoGetMYuiMedalBonusConditionsResponse(tbl) + return resp.make() + + async def handle_d5f4(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gasha_medals + tbl = self.load_data_csv('GashaMedals') + resp = SaoGetMGashaMedalsResponse(tbl) + return resp.make() + + async def handle_d5f6(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gasha_medal_types + tbl = self.load_data_csv('GashaMedalTypes') + resp = SaoGetMGashaMedalTypesResponse(tbl) + return resp.make() + + async def handle_d5f8(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gasha_medal_settings + req = SaoGetMGashaMedalSettingsRequest(header, request) + tbl = self.load_data_csv('GashaMedalSettings') + resp = SaoGetMGashaMedalSettingsResponse(tbl) + return resp.make() + + async def handle_d5fa(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gasha_medal_bonuses + tbl = self.load_data_csv('GashaMedalBonuses') + resp = SaoGetMGashaMedalBonusesResponse(tbl) + return resp.make() + + async def handle_d5fc(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: # master_data/get_m_gasha_medal_shops - req = GetMGashaMedalShopsRequest(header, request) - resp = GetMGashaMedalShopsResponse(header.cmd + 1) - - shops = self.load_data_csv("GashaMedalShops") - for shop in shops: - tmp = GashaMedalShop.from_args(int(shop['GashaMedalShopId']), shop['Name'], int(shop['GashaMedalId']), int(shop['UseGashaMedalNum']), int(shop['PurchaseLimit'])) - tmp.sales_end_date = datetime(2121, 1, 1, 0, 0, 0, 0) # always open - - resp.data_list.append(tmp) - - self.logger.debug(f"Load {len(resp.data_list)} Gasha Medal Shops") + tbl = self.load_data_csv('GashaMedalShops') + resp = SaoGetMGashaMedalShopsResponse(tbl) return resp.make() - - async def handle_d5fe(self, header: SaoRequestHeader, request: bytes) -> bytes: - # master_data/get_m_gasha_medal_shop_items - return SaoNoopResponse(header.cmd + 1).make() - - async def handle_d604(self, header: SaoRequestHeader, request: bytes) -> bytes: - # master_data_2/get_m_res_earn_campaign_shops - req = GetMResEarnCampaignShopsRequest(header, request) - resp = GetMResEarnCampaignShopsResponse(header.cmd + 1) - - shops = self.load_data_csv("ResEarnCampaignShops") - for shop in shops: - tmp = ResEarnCampaignShop.from_args(int(shop['ResEarnCampaignShopId']), int(shop['ResEarnCampaignApplicationId']), shop['Name']) - tmp.selling_yui_medal = int(shop['SellingYuiMedal']) - tmp.selling_col = int(shop['SellingCol']) - tmp.selling_event_item_id = int(shop['SellingEventItemId']) - tmp.selling_event_item_num = int(shop['SellingEventItemNum']) - tmp.purchase_limit = int(shop['PurchaseLimit']) - tmp.get_application_point = int(shop['GetApplicationPoint']) - - tmp.sales_end_date = datetime(2121, 1, 1, 0, 0, 0, 0) # always open - - resp.data_list.append(tmp) - #self.logger.debug(f"Load {len(resp.data_list)} Res Earn Campaign Shops") - return SaoNoopResponse(header.cmd + 1).make() - - async def handle_d606(self, header: SaoRequestHeader, request: bytes) -> bytes: - # master_data_2/get_m_res_earn_campaign_shop_items - return SaoNoopResponse(header.cmd + 1).make() - - async def handle_c108(self, header: SaoRequestHeader, request: bytes) -> bytes: - # common/logout_ticket_unpurchased - req = SaoLogoutTicketUnpurchasedRequest(header, request) - return SaoLogoutTicketUnpurchasedResponse(header.cmd + 1).make() - - async def handle_cb02(self, header: SaoRequestHeader, request: bytes) -> bytes: - # quest_ranking/get_quest_hierarchy_progress_degrees_ranking_list - req = SaoGetQuestHierarchyProgressDegreesRankingListRequest(header, request) - return SaoGetQuestHierarchyProgressDegreesRankingListResponse(header.cmd + 1).make() - - async def handle_cb04(self, header: SaoRequestHeader, request: bytes) -> bytes: - # quest_ranking/get_quest_popular_hero_log_ranking_list - req = SaoGetQuestPopularHeroLogRankingListRequest(header, request) - return SaoGetQuestPopularHeroLogRankingListResponse(header.cmd + 1).make() + async def handle_d5fe(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data/get_m_gasha_medal_shop_items + req = SaoGetMGashaMedalShopItemsRequest(header, request) + tbl = self.load_data_csv('GashaMedalShopItems') + resp = SaoGetMGashaMedalShopItemsResponse(tbl) + return resp.make() + + async def handle_d600(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mres_earn_campaign_applications + tbl = self.load_data_csv('ResEarnCampaignApplications') + resp = SaoGetMResEarnCampaignApplicationsResponse(tbl) + return resp.make() + + async def handle_d602(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mres_earn_campaign_application_products + req = SaoGetMResEarnCampaignApplicationProductsRequest(header, request) + tbl = self.load_data_csv('ResEarnCampaignApplicationProducts') + resp = SaoGetMResEarnCampaignApplicationProductsResponse(tbl) + return resp.make() + + async def handle_d604(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mres_earn_campaign_shops + tbl = self.load_data_csv('ResEarnCampaignShops') + resp = SaoGetMResEarnCampaignShopsResponse(tbl) + return resp.make() + + async def handle_d606(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mres_earn_campaign_shop_items + req = SaoGetMResEarnCampaignShopItemsRequest(header, request) + tbl = self.load_data_csv('ResEarnCampaignShopItems') + resp = SaoGetMResEarnCampaignShopItemsResponse(tbl) + return resp.make() + + async def handle_d608(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mpaying_yui_medal_bonuses + tbl = self.load_data_csv('PayingYuiMedalBonuses') + resp = SaoGetMPayingYuiMedalBonusesResponse(tbl) + return resp.make() + + async def handle_d60a(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mac_login_bonuses + tbl = self.load_data_csv('AcLoginBonuses') + resp = SaoGetMAcLoginBonusesResponse(tbl) + return resp.make() + + async def handle_d60c(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mplay_campaigns + tbl = self.load_data_csv('PlayCampaigns') + resp = SaoGetMPlayCampaignsResponse(tbl) + return resp.make() + + async def handle_d60e(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mplay_campaign_rewards + req = SaoGetMPlayCampaignRewardsRequest(header, request) + tbl = self.load_data_csv('PlayCampaignRewards') + resp = SaoGetMPlayCampaignRewardsResponse(tbl) + return resp.make() + + async def handle_d610(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mgasha_free_campaigns + tbl = self.load_data_csv('GashaFreeCampaigns') + resp = SaoGetMGashaFreeCampaignsResponse(tbl) + return resp.make() + + async def handle_d612(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mquest_drop_boost_campaigns + tbl = self.load_data_csv('QuestDropBoostCampaigns') + resp = SaoGetMQuestDropBoostCampaignsResponse(tbl) + return resp.make() + + async def handle_d614(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mfirst_ticket_purchase_campaigns + tbl = self.load_data_csv('FirstTicketPurchaseCampaigns') + resp = SaoGetMFirstTicketPurchaseCampaignsResponse(tbl) + return resp.make() + + async def handle_d616(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mlinked_site_reg_campaigns + tbl = self.load_data_csv('LinkedSiteRegCampaigns') + resp = SaoGetMLinkedSiteRegCampaignsResponse(tbl) + return resp.make() + + async def handle_d618(self, header: SaoRequestHeader, request: bytes, src_ip: str) -> bytes: + # master_data2/get_mlinked_site_reg_campaign_rewards + req = SaoGetMLinkedSiteRegCampaignRewardsRequest(header, request) + tbl = self.load_data_csv('LinkedSiteRegCampaignRewards') + resp = SaoGetMLinkedSiteRegCampaignRewardsResponse(tbl) + return resp.make() diff --git a/titles/sao/config.py b/titles/sao/config.py index dfae3c0..892fdca 100644 --- a/titles/sao/config.py +++ b/titles/sao/config.py @@ -30,9 +30,21 @@ class SaoServerConfig: ) @property - def use_https(self) -> bool: + def photon_app_id(self) -> str: return CoreConfig.get_config_field( - self.__config, "sao", "server", "use_https", default=False + self.__config, "sao", "server", "photon_app_id", default="7df3a2f6-d69d-4073-aafe-810ee61e1cea" + ) + + @property + def data_version(self) -> int: + return CoreConfig.get_config_field( + self.__config, "sao", "server", "data_version", default=1 + ) + + @property + def game_version(self) -> int: + return CoreConfig.get_config_field( + self.__config, "sao", "server", "game_version", default=33 ) class SaoCryptConfig: @@ -50,12 +62,6 @@ class SaoCryptConfig: return CoreConfig.get_config_field( self.__config, "sao", "crypt", "key", default="" ) - - @property - def iv(self) -> str: - return CoreConfig.get_config_field( - self.__config, "sao", "crypt", "iv", default="" - ) class SaoHashConfig: def __init__(self, parent_config: "SaoConfig"): @@ -73,9 +79,31 @@ class SaoHashConfig: self.__config, "sao", "hash", "hash_base", default="" ) +class SaoCardConfig: + def __init__(self, parent_config: "SaoConfig"): + self.__config = parent_config + + @property + def enable(self) -> bool: + return CoreConfig.get_config_field( + self.__config, "sao", "card", "enable", default=True + ) + + @property + def crypt_password(self) -> str: + return CoreConfig.get_config_field( + self.__config, "sao", "card", "crypt_password", default="" + ) + + @property + def crypt_salt(self) -> str: + return CoreConfig.get_config_field( + self.__config, "sao", "card", "crypt_salt", default="" + ) class SaoConfig(dict): def __init__(self) -> None: self.server = SaoServerConfig(self) self.crypt = SaoCryptConfig(self) self.hash = SaoHashConfig(self) + self.card = SaoCardConfig(self) diff --git a/titles/sao/const.py b/titles/sao/const.py index d181556..d687c64 100644 --- a/titles/sao/const.py +++ b/titles/sao/const.py @@ -1,5 +1,4 @@ -from enum import Enum - +from enum import IntEnum class SaoConstants: GAME_CODE = "SDEW" @@ -9,8 +8,8 @@ class SaoConstants: VER_SAO = 0 - VERSION_NAMES = ("Sword Art Online Arcade") - + VERSION_NAMES = ("Sword Art Online Arcade",) + SERIAL_IDENT_SATALITE = 4 SERIAL_IDENT_TERMINAL = 5 @@ -23,3 +22,653 @@ class SaoConstants: @classmethod def game_ver_to_string(cls, ver: int): return cls.VERSION_NAMES[ver] + +class RewardType(IntEnum): + None_ = 0 + HeroLog = 1 + Equipment = 2 + Item = 3 + Col = 4 + VP = 5 + YuiMadal = 6 + VPGashaTicket = 7 + SupportLog = 8 + EpisodeAppend = 9 + EventItem = 10 + Ticket = 11 + +class ItemType(IntEnum): + ADD_HERO_PROPERTY = 1 + ADD_WEAPON_PROPERTY = 2 + ADD_ARMOR_PROPERTY = 3 + UNLOCK_PROPERTY = 4 + REMOVE_PROPERTY = 5 + ADD_SKILL_SLOT = 6 + ADD_XP = 7 + REDEMPTION = 8 + HERO_LEVEL_LIMIT_BREAK = 9 + WEAPON_LEVEL_LIMIT_BREAK = 10 + ARMOR_LEVEL_LIMIT_BREAK = 11 + ADD_AWAKENING_XP = 12 + +class ExBonusCondition(IntEnum): + CLEAR_UNDER_X_SECS = 1 + DEFEAT_X_MONSTER_Y_TIMES = 2 + DEFEAT_X_MONSTERS = 3 + CLEAR_X_MISSIONS = 4 + CLEAR_MISSION_DIFFICULTY_X = 5 + COLLECT_X_LOGS = 6 + CLEAR_SKILL_LEVEL_X = 7 + NO_LOSSES = 8 + ACCEL_X_TIMES = 9 + MAX_COMBO_X = 10 + MULTIPLAYER_CLEAR_X = 11 + +class UnanalyzedLogGrade(IntEnum): + WHITE = 1 + COPPER = 2 + SILVER = 3 + GOLD = 4 + RAINBOW = 5 + +class QuestType(IntEnum): + EPISODE = 1 + TRIAL_TOWER = 2 + SIDE = 3 + VERSUS = 4 + EX_TOWER = 5 + EPISODE_CHAPTER = 6 # Unused + +class GameconnectCmd(IntEnum): + TICKET_REQUEST = 0xC000 + TICKET_RESPONSE = 0xC001 + GET_APP_VERSIONS_REQUEST = 0xC100 + GET_APP_VERSIONS_RESPONSE = 0xC101 + MASTER_DATA_VERSION_CHECK_REQUEST = 0xC102 + MASTER_DATA_VERSION_CHECK_RESPONSE = 0xC103 + LOGIN_REQUEST = 0xC104 + LOGIN_RESPONSE = 0xC105 + LOGOUT_REQUEST = 0xC106 + LOGOUT_RESPONSE = 0xC107 + LOGOUT_TICKET_UNPURCHASED_REQUEST = 0xC108 + LOGOUT_TICKET_UNPURCHASED_RESPONSE = 0xC109 + PAYING_PLAY_START_REQUEST = 0xC10A + PAYING_PLAY_START_RESPONSE = 0xC10B + PAYING_PLAY_END_REQUEST = 0xC10C + PAYING_PLAY_END_RESPONSE = 0xC10D + PURCHASE_TICKET_REQUEST = 0xC10E + PURCHASE_TICKET_RESPONSE = 0xC10F + CONSUME_TICKET_REQUEST = 0xC110 + CONSUME_TICKET_RESPONSE = 0xC111 + ADD_CREDIT_REQUEST = 0xC112 + ADD_CREDIT_RESPONSE = 0xC113 + CONSUME_CREDIT_REQUEST = 0xC114 + CONSUME_CREDIT_RESPONSE = 0xC115 + PURCHASE_TICKET_GUEST_REQUEST = 0xC116 + PURCHASE_TICKET_GUEST_RESPONSE = 0xC117 + CONSUME_TICKET_GUEST_REQUEST = 0xC118 + CONSUME_TICKET_GUEST_RESPONSE = 0xC119 + ADD_CREDIT_GUEST_REQUEST = 0xC11A + ADD_CREDIT_GUEST_RESPONSE = 0xC11B + CONSUME_CREDIT_GUEST_REQUEST = 0xC11C + CONSUME_CREDIT_GUEST_RESPONSE = 0xC11D + GET_AUTH_CARD_DATA_REQUEST = 0xC11E + GET_AUTH_CARD_DATA_RESPONSE = 0xC11F + GET_ACCESS_CODE_BY_KEITAI_REQUEST = 0xC120 + GET_ACCESS_CODE_BY_KEITAI_RESPONSE = 0xC121 + GET_MAINTENANCE_INFO_REQUEST = 0xC122 + GET_MAINTENANCE_INFO_RESPONSE = 0xC123 + GET_RESOURCE_PATH_INFO_REQUEST = 0xC124 + GET_RESOURCE_PATH_INFO_RESPONSE = 0xC125 + VALIDATION_ERROR_NOTIFICATION_REQUEST = 0xC126 + VALIDATION_ERROR_NOTIFICATION_RESPONSE = 0xC127 + POWER_CUTTING_RETURN_NOTIFICATION_REQUEST = 0xC128 + POWER_CUTTING_RETURN_NOTIFICATION_RESPONSE = 0xC129 + GIVE_FREE_TICKET_REQUEST = 0xC12A + GIVE_FREE_TICKET_RESPONSE = 0xC12B + MATCHING_ERROR_NOTIFICATION_REQUEST = 0xC12C + MATCHING_ERROR_NOTIFICATION_RESPONSE = 0xC12D + AC_CABINET_BOOT_NOTIFICATION_REQUEST = 0xC12E + AC_CABINET_BOOT_NOTIFICATION_RESPONSE = 0xC12F + FIRST_TUTORIAL_END_REQUEST = 0xC200 + FIRST_TUTORIAL_END_RESPONSE = 0xC201 + VARIOUS_TUTORIAL_END_REQUEST = 0xC202 + VARIOUS_TUTORIAL_END_RESPONSE = 0xC203 + GET_VARIOUS_TUTORIAL_DATA_LIST_REQUEST = 0xC204 + GET_VARIOUS_TUTORIAL_DATA_LIST_RESPONSE = 0xC205 + DISCHARGE_PROFILE_CARD_REQUEST = 0xC300 + DISCHARGE_PROFILE_CARD_RESPONSE = 0xC301 + DISCHARGE_RESOURCE_CARD_REQUEST = 0xC302 + DISCHARGE_RESOURCE_CARD_RESPONSE = 0xC303 + DISCHARGE_RESOURCE_CARD_COMPLETE_REQUEST = 0xC304 + DISCHARGE_RESOURCE_CARD_COMPLETE_RESPONSE = 0xC305 + SCAN_QR_QUEST_PROFILE_CARD_REQUEST = 0xC306 + SCAN_QR_QUEST_PROFILE_CARD_RESPONSE = 0xC307 + SCAN_QR_SHOP_RESOURCE_CARD_REQUEST = 0xC308 + SCAN_QR_SHOP_RESOURCE_CARD_RESPONSE = 0xC309 + SCAN_QR_QUEST_RESOURCE_CARD_REQUEST = 0xC30A + SCAN_QR_QUEST_RESOURCE_CARD_RESPONSE = 0xC30B + CHECK_YUI_MEDAL_GET_CONDITION_REQUEST = 0xC400 + CHECK_YUI_MEDAL_GET_CONDITION_RESPONSE = 0xC401 + GET_YUI_MEDAL_BONUS_USER_DATA_REQUEST = 0xC402 + GET_YUI_MEDAL_BONUS_USER_DATA_RESPONSE = 0xC403 + CHECK_COMEBACK_EVENT_REQUEST = 0xC404 + CHECK_COMEBACK_EVENT_RESPONSE = 0xC405 + CHANGE_MY_STORE_REQUEST = 0xC406 + CHANGE_MY_STORE_RESPONSE = 0xC407 + CHECK_TITLE_GET_DECISION_REQUEST = 0xC408 + CHECK_TITLE_GET_DECISION_RESPONSE = 0xC409 + CHECK_PROFILE_CARD_USED_REWARD_REQUEST = 0xC40A + CHECK_PROFILE_CARD_USED_REWARD_RESPONSE = 0xC40B + CHECK_AC_LOGIN_BONUS_REQUEST = 0xC40C + CHECK_AC_LOGIN_BONUS_RESPONSE = 0xC40D + GET_USER_BASIC_DATA_REQUEST = 0xC500 + GET_USER_BASIC_DATA_RESPONSE = 0xC501 + GET_VP_GASHA_TICKET_DATA_LIST_REQUEST = 0xC502 + GET_VP_GASHA_TICKET_DATA_LIST_RESPONSE = 0xC503 + GET_PRESENT_BOX_NUM_REQUEST = 0xC504 + GET_PRESENT_BOX_NUM_RESPONSE = 0xC505 + GET_HERO_LOG_USER_DATA_LIST_REQUEST = 0xC600 + GET_HERO_LOG_USER_DATA_LIST_RESPONSE = 0xC601 + GET_EQUIPMENT_USER_DATA_LIST_REQUEST = 0xC602 + GET_EQUIPMENT_USER_DATA_LIST_RESPONSE = 0xC603 + GET_ITEM_USER_DATA_LIST_REQUEST = 0xC604 + GET_ITEM_USER_DATA_LIST_RESPONSE = 0xC605 + GET_SUPPORT_LOG_USER_DATA_LIST_REQUEST = 0xC606 + GET_SUPPORT_LOG_USER_DATA_LIST_RESPONSE = 0xC607 + GET_EPISODE_APPEND_DATA_LIST_REQUEST = 0xC608 + GET_EPISODE_APPEND_DATA_LIST_RESPONSE = 0xC609 + GET_EVENT_ITEM_DATA_LIST_REQUEST = 0xC60A + GET_EVENT_ITEM_DATA_LIST_RESPONSE = 0xC60B + GET_GASHA_MEDAL_USER_DATA_LIST_REQUEST = 0xC60C + GET_GASHA_MEDAL_USER_DATA_LIST_RESPONSE = 0xC60D + GET_SHOP_RESOURCE_SALES_DATA_LIST_REQUEST = 0xC700 + GET_SHOP_RESOURCE_SALES_DATA_LIST_RESPONSE = 0xC701 + PURCHASE_SHOP_RESOURCE_REQUEST = 0xC702 + PURCHASE_SHOP_RESOURCE_RESPONSE = 0xC703 + DISCARD_SHOP_RESOURCE_REQUEST = 0xC704 + DISCARD_SHOP_RESOURCE_RESPONSE = 0xC705 + GET_TITLE_USER_DATA_LIST_REQUEST = 0xC800 + GET_TITLE_USER_DATA_LIST_RESPONSE = 0xC801 + CHANGE_TITLE_REQUEST = 0xC802 + CHANGE_TITLE_RESPONSE = 0xC803 + GET_PARTY_DATA_LIST_REQUEST = 0xC804 + GET_PARTY_DATA_LIST_RESPONSE = 0xC805 + CHANGE_PARTY_REQUEST = 0xC806 + CHANGE_PARTY_RESPONSE = 0xC807 + GET_SUPPORT_LOG_PARTY_DATA_LIST_REQUEST = 0xC808 + GET_SUPPORT_LOG_PARTY_DATA_LIST_RESPONSE = 0xC809 + CHANGE_SUPPORT_LOG_PARTY_REQUEST = 0xC80A + CHANGE_SUPPORT_LOG_PARTY_RESPONSE = 0xC80B + CHANGE_HERO_LOG_LAST_SETTING_SKILL_REQUEST = 0xC80C + CHANGE_HERO_LOG_LAST_SETTING_SKILL_RESPONSE = 0xC80D + LOCK_RESOURCE_REQUEST = 0xC80E + LOCK_RESOURCE_RESPONSE = 0xC80F + UNLOCK_RESOURCE_REQUEST = 0xC810 + UNLOCK_RESOURCE_RESPONSE = 0xC811 + DISPOSAL_RESOURCE_REQUEST = 0xC812 + DISPOSAL_RESOURCE_RESPONSE = 0xC813 + SYNTHESIZE_ENHANCEMENT_HERO_LOG_REQUEST = 0xC814 + SYNTHESIZE_ENHANCEMENT_HERO_LOG_RESPONSE = 0xC815 + SYNTHESIZE_ENHANCEMENT_EQUIPMENT_REQUEST = 0xC816 + SYNTHESIZE_ENHANCEMENT_EQUIPMENT_RESPONSE = 0xC817 + SYNTHESIZE_ENHANCEMENT_SUPPORT_LOG_REQUEST = 0xC818 + SYNTHESIZE_ENHANCEMENT_SUPPORT_LOG_RESPONSE = 0xC819 + SYNTHESIZE_ABILITY_HERO_LOG_REQUEST = 0xC81A + SYNTHESIZE_ABILITY_HERO_LOG_RESPONSE = 0xC81B + SYNTHESIZE_ABILITY_EQUIPMENT_REQUEST = 0xC81C + SYNTHESIZE_ABILITY_EQUIPMENT_RESPONSE = 0xC81D + SYNTHESIZE_ABILITY_SUPPORT_LOG_REQUEST = 0xC81E + SYNTHESIZE_ABILITY_SUPPORT_LOG_RESPONSE = 0xC820 + GET_QUEST_SCENE_USER_DATA_LIST_REQUEST = 0xC900 + GET_QUEST_SCENE_USER_DATA_LIST_RESPONSE = 0xC901 + GET_QUEST_SCENE_PREV_SCAN_PROFILE_CARD_REQUEST = 0xC902 + GET_QUEST_SCENE_PREV_SCAN_PROFILE_CARD_RESPONSE = 0xC903 + EPISODE_PLAY_START_REQUEST = 0xC904 + EPISODE_PLAY_START_RESPONSE = 0xC905 + EPISODE_PLAY_CONTINUE_REQUEST = 0xC906 + EPISODE_PLAY_CONTINUE_RESPONSE = 0xC907 + EPISODE_PLAY_END_REQUEST = 0xC908 + EPISODE_PLAY_END_RESPONSE = 0xC909 + EPISODE_PLAY_END_UNANALYZED_LOG_FIXED_REQUEST = 0xC90A + EPISODE_PLAY_END_UNANALYZED_LOG_FIXED_RESPONSE = 0xC90B + SIDE_QUEST_PLAY_START_REQUEST = 0xC90C + SIDE_QUEST_PLAY_START_RESPONSE = 0xC90D + SIDE_QUEST_PLAY_CONTINUE_REQUEST = 0xC90E + SIDE_QUEST_PLAY_CONTINUE_RESPONSE = 0xC90F + SIDE_QUEST_PLAY_END_REQUEST = 0xC910 + SIDE_QUEST_PLAY_END_RESPONSE = 0xC911 + SIDE_QUEST_PLAY_END_UNANALYZED_LOG_FIXED_REQUEST = 0xC912 + SIDE_QUEST_PLAY_END_UNANALYZED_LOG_FIXED_RESPONSE = 0xC913 + TRIAL_TOWER_PLAY_START_REQUEST = 0xC914 + TRIAL_TOWER_PLAY_START_RESPONSE = 0xC915 + TRIAL_TOWER_PLAY_CONTINUE_REQUEST = 0xC916 + TRIAL_TOWER_PLAY_CONTINUE_RESPONSE = 0xC917 + TRIAL_TOWER_PLAY_END_REQUEST = 0xC918 + TRIAL_TOWER_PLAY_END_RESPONSE = 0xC919 + TRIAL_TOWER_PLAY_END_UNANALYZED_LOG_FIXED_REQUEST = 0xC91A + TRIAL_TOWER_PLAY_END_UNANALYZED_LOG_FIXED_RESPONSE = 0xC91B + DEFRAG_MATCH_QUEST_PLAY_START_REQUEST = 0xC91C + DEFRAG_MATCH_QUEST_PLAY_START_RESPONSE = 0xC91D + DEFRAG_MATCH_QUEST_PLAY_END_REQUEST = 0xC91E + DEFRAG_MATCH_QUEST_PLAY_END_RESPONSE = 0xC91F + DEFRAG_MATCH_QUEST_PLAY_END_UNANALYZED_LOG_FIXED_REQUEST = 0xC920 + DEFRAG_MATCH_QUEST_PLAY_END_UNANALYZED_LOG_FIXED_RESPONSE = 0xC921 + EX_TOWER_QUEST_PLAY_START_REQUEST = 0xC922 + EX_TOWER_QUEST_PLAY_START_RESPONSE = 0xC923 + EX_TOWER_QUEST_PLAY_CONTINUE_REQUEST = 0xC924 + EX_TOWER_QUEST_PLAY_CONTINUE_RESPONSE = 0xC925 + EX_TOWER_QUEST_PLAY_END_REQUEST = 0xC926 + EX_TOWER_QUEST_PLAY_END_RESPONSE = 0xC927 + EX_TOWER_QUEST_PLAY_END_UNANALYZED_LOG_FIXED_REQUEST = 0xC928 + EX_TOWER_QUEST_PLAY_END_UNANALYZED_LOG_FIXED_RESPONSE = 0xC929 + GET_EX_TOWER_USER_DATA_REQUEST = 0xC92A + GET_EX_TOWER_USER_DATA_RESPONSE = 0xC92B + REG_EX_TOWER_HALL_OF_FAME_CONFIRM_FLAG_REQUEST = 0xC92C + REG_EX_TOWER_HALL_OF_FAME_CONFIRM_FLAG_RESPONSE = 0xC92D + REG_EX_TOWER_CONFIRM_FLAG_REQUEST = 0xC92E + REG_EX_TOWER_CONFIRM_FLAG_RESPONSE = 0xC92F + GET_CHAT_SIDE_STORY_USER_DATA_LIST_REQUEST = 0xC930 + GET_CHAT_SIDE_STORY_USER_DATA_LIST_RESPONSE = 0xC931 + GET_USER_QUEST_EPISODE_STATUS_DATA_LIST_REQUEST = 0xC932 + GET_USER_QUEST_EPISODE_STATUS_DATA_LIST_RESPONSE = 0xC933 + APPLY_QUEST_DROP_BOOST_REQUEST = 0xC934 + APPLY_QUEST_DROP_BOOST_RESPONSE = 0xC935 + CREATE_QUEST_SCENE_MULTI_PLAY_ROOM_ID_REQUEST = 0xCA00 + CREATE_QUEST_SCENE_MULTI_PLAY_ROOM_ID_RESPONSE = 0xCA01 + GET_QUEST_SCENE_MULTI_PLAY_PHOTON_SERVER_REQUEST = 0xCA02 + GET_QUEST_SCENE_MULTI_PLAY_PHOTON_SERVER_RESPONSE = 0xCA03 + GET_QUEST_SCENE_MULTI_PLAY_PHOTON_SERVER_BY_ROOM_REQUEST = 0xCA04 + GET_QUEST_SCENE_MULTI_PLAY_PHOTON_SERVER_BY_ROOM_RESPONSE = 0xCA05 + GET_QUEST_BEST_SOCRE_RANKING_LIST_REQUEST = 0xCB00 + GET_QUEST_BEST_SOCRE_RANKING_LIST_RESPONSE = 0xCB01 + GET_QUEST_HIERARCHY_PROGRESS_DEGREES_RANKING_LIST_REQUEST = 0xCB02 + GET_QUEST_HIERARCHY_PROGRESS_DEGREES_RANKING_LIST_RESPONSE = 0xCB03 + GET_QUEST_POPULAR_HERO_LOG_RANKING_LIST_REQUEST = 0xCB04 + GET_QUEST_POPULAR_HERO_LOG_RANKING_LIST_RESPONSE = 0xCB05 + GET_QUEST_EX_TOWER_HIERARCHY_PROGRESS_DEGREES_STORE_RANKING_LIST_REQUEST = 0xCB06 + GET_QUEST_EX_TOWER_HIERARCHY_PROGRESS_DEGREES_STORE_RANKING_LIST_RESPONSE = 0xCB07 + GET_QUEST_EX_TOWER_HIERARCHY_PROGRESS_DEGREES_NATIONAL_RANKING_LIST_REQUEST = 0xCB08 + GET_QUEST_EX_TOWER_HIERARCHY_PROGRESS_DEGREES_NATIONAL_RANKING_LIST_RESPONSE = 0xCB09 + GET_TREASURE_HUNT_BASIC_DATA_REQUEST = 0xCC00 + GET_TREASURE_HUNT_BASIC_DATA_RESPONSE = 0xCC01 + GET_TREASURE_HUNT_WHOLE_TASK_DATA_LIST_REQUEST = 0xCC02 + GET_TREASURE_HUNT_WHOLE_TASK_DATA_LIST_RESPONSE = 0xCC03 + GET_TREASURE_HUNT_INDIVIDUAL_TASK_DATA_LIST_REQUEST = 0xCC04 + GET_TREASURE_HUNT_INDIVIDUAL_TASK_DATA_LIST_RESPONSE = 0xCC05 + TREASURE_HUNT_AD_CONFIRM_NOTIFICATION_REQUEST = 0xCC06 + TREASURE_HUNT_AD_CONFIRM_NOTIFICATION_RESPONSE = 0xCC07 + GET_TREASURE_HUNT_EVENT_POINT_RANKING_USER_DATA_REQUEST = 0xCC08 + GET_TREASURE_HUNT_EVENT_POINT_RANKING_USER_DATA_RESPONSE = 0xCC09 + GET_TREASURE_HUNT_EVENT_POINT_STORE_RANKING_LIST_REQUEST = 0xCC0A + GET_TREASURE_HUNT_EVENT_POINT_STORE_RANKING_LIST_RESPONSE = 0xCC0B + GET_TREASURE_HUNT_EVENT_POINT_NATIONAL_RANKING_LIST_REQUEST = 0xCC0C + GET_TREASURE_HUNT_EVENT_POINT_NATIONAL_RANKING_LIST_RESPONSE = 0xCC0D + GET_DEFRAG_MATCH_BASIC_DATA_REQUEST = 0xCD00 + GET_DEFRAG_MATCH_BASIC_DATA_RESPONSE = 0xCD01 + GET_DEFRAG_MATCH_RANKING_USER_DATA_REQUEST = 0xCD02 + GET_DEFRAG_MATCH_RANKING_USER_DATA_RESPONSE = 0xCD03 + GET_DEFRAG_MATCH_LEAGUE_POINT_RANKING_LIST_REQUEST = 0xCD04 + GET_DEFRAG_MATCH_LEAGUE_POINT_RANKING_LIST_RESPONSE = 0xCD05 + GET_DEFRAG_MATCH_LEAGUE_SCORE_RANKING_LIST_REQUEST = 0xCD06 + GET_DEFRAG_MATCH_LEAGUE_SCORE_RANKING_LIST_RESPONSE = 0xCD07 + DEFRAG_MATCH_AD_CONFIRM_NOTIFICATION_REQUEST = 0xCD08 + DEFRAG_MATCH_AD_CONFIRM_NOTIFICATION_RESPONSE = 0xCD09 + CHECK_GET_SEED_DEFRAG_MATCH_REQUEST = 0xCD0A + CHECK_GET_SEED_DEFRAG_MATCH_RESPONSE = 0xCD0B + REG_DEFRAG_MATCH_HALL_OF_FAME_CONFIRM_FLAG_REQUEST = 0xCD0C + REG_DEFRAG_MATCH_HALL_OF_FAME_CONFIRM_FLAG_RESPONSE = 0xCD0D + GET_EVENT_SCENE_USER_DATA_LIST_REQUEST = 0xCE00 + GET_EVENT_SCENE_USER_DATA_LIST_RESPONSE = 0xCE01 + REG_PLAYED_EVENT_SCENE_USER_DATA_LIST_REQUEST = 0xCE02 + REG_PLAYED_EVENT_SCENE_USER_DATA_LIST_RESPONSE = 0xCE03 + GET_GASHA_LIST_REQUEST = 0xCF00 + GET_GASHA_LIST_RESPONSE = 0xCF01 + EXEC_GASHA_REQUEST = 0xCF02 + EXEC_GASHA_RESPONSE = 0xCF03 + EXEC_CREDIT_GASHA_START_REQUEST = 0xCF04 + EXEC_CREDIT_GASHA_START_RESPONSE = 0xCF05 + EXEC_CREDIT_GASHA_EXTRA_PAYING_REQUEST = 0xCF06 + EXEC_CREDIT_GASHA_EXTRA_PAYING_RESPONSE = 0xCF07 + EXEC_CREDIT_GASHA_END_REQUEST = 0xCF08 + EXEC_CREDIT_GASHA_END_RESPONSE = 0xCF09 + EXEC_CREDIT_GASHA_CARD_DISCHARGE_STATE_MIGRATION_REQUEST = 0xCF0A + EXEC_CREDIT_GASHA_CARD_DISCHARGE_STATE_MIGRATION_RESPONSE = 0xCF0B + EXEC_CREDIT_GASHA_CARD_DISCHARGE_END_REQUEST = 0xCF0C + EXEC_CREDIT_GASHA_CARD_DISCHARGE_END_RESPONSE = 0xCF0D + GET_GASHA_MEDAL_SHOP_USER_DATA_LIST_REQUEST = 0xCF0E + GET_GASHA_MEDAL_SHOP_USER_DATA_LIST_RESPONSE = 0xCF0F + PURCHASE_GASHA_MEDAL_SHOP_ITEM_REQUEST = 0xCF10 + PURCHASE_GASHA_MEDAL_SHOP_ITEM_RESPONSE = 0xCF11 + GET_GASHA_FREE_CAMPAIGN_USER_DATA_REQUEST = 0xCF12 + GET_GASHA_FREE_CAMPAIGN_USER_DATA_RESPONSE = 0xCF13 + GET_ADVENTURE_EXEC_USER_DATA_REQUEST = 0xD000 + GET_ADVENTURE_EXEC_USER_DATA_RESPONSE = 0xD001 + GET_ADVENTURE_PARTY_DATA_LIST_REQUEST = 0xD002 + GET_ADVENTURE_PARTY_DATA_LIST_RESPONSE = 0xD003 + GET_YUI_MEDAL_SHOP_USER_DATA_LIST_REQUEST = 0xD100 + GET_YUI_MEDAL_SHOP_USER_DATA_LIST_RESPONSE = 0xD101 + PURCHASE_YUI_MEDAL_SHOP_ITEM_REQUEST = 0xD102 + PURCHASE_YUI_MEDAL_SHOP_ITEM_RESPONSE = 0xD103 + PURCHASE_EVENT_SHOP_ITEM_REQUEST = 0xD104 + PURCHASE_EVENT_SHOP_ITEM_RESPONSE = 0xD105 + GET_BEGINNER_MISSION_USER_DATA_REQUEST = 0xD200 + GET_BEGINNER_MISSION_USER_DATA_RESPONSE = 0xD201 + GET_BEGINNER_MISSION_PROGRESSES_USER_DATA_LIST_REQUEST = 0xD202 + GET_BEGINNER_MISSION_PROGRESSES_USER_DATA_LIST_RESPONSE = 0xD203 + GET_BEGINNER_MISSION_SEAT_PROGRESSES_USER_DATA_LIST_REQUEST = 0xD204 + GET_BEGINNER_MISSION_SEAT_PROGRESSES_USER_DATA_LIST_RESPONSE = 0xD205 + BEGINNER_MISSION_AD_CONFIRM_NOTIFICATION_REQUEST = 0xD206 + BEGINNER_MISSION_AD_CONFIRM_NOTIFICATION_RESPONSE = 0xD207 + RECEIVE_BEGINNER_MISSION_REWARD_REQUEST = 0xD208 + RECEIVE_BEGINNER_MISSION_REWARD_RESPONSE = 0xD209 + GET_RES_EARN_CAMPAIGN_SHOP_USER_DATA_LIST_REQUEST = 0xD300 + GET_RES_EARN_CAMPAIGN_SHOP_USER_DATA_LIST_RESPONSE = 0xD301 + PURCHASE_RES_EARN_CAMPAIGN_SHOP_ITEM_REQUEST = 0xD302 + PURCHASE_RES_EARN_CAMPAIGN_SHOP_ITEM_RESPONSE = 0xD303 + PAYING_YUI_MEDAL_BONUS_GET_CHECK_REQUEST = 0xD304 + PAYING_YUI_MEDAL_BONUS_GET_CHECK_RESPONSE = 0xD305 + PAYING_YUI_MEDAL_BONUS_AD_CONFIRM_NOTIFICATION_REQUEST = 0xD306 + PAYING_YUI_MEDAL_BONUS_AD_CONFIRM_NOTIFICATION_RESPONSE = 0xD307 + GET_PLAY_CAMPAIGN_USER_DATA_LIST_REQUEST = 0xD308 + GET_PLAY_CAMPAIGN_USER_DATA_LIST_RESPONSE = 0xD309 + GET_PLAY_CAMPAIGN_STORE_USER_DATA_LIST_REQUEST = 0xD30A + GET_PLAY_CAMPAIGN_STORE_USER_DATA_LIST_RESPONSE = 0xD30B + GET_PLAY_CAMPAIGN_REWARD_USER_DATA_LIST_REQUEST = 0xD30C + GET_PLAY_CAMPAIGN_REWARD_USER_DATA_LIST_RESPONSE = 0xD30D + APPLY_FIRST_TICKET_PURCHASE_CAMPAIGN_REQUEST = 0xD30E + APPLY_FIRST_TICKET_PURCHASE_CAMPAIGN_RESPONSE = 0xD30F + GET_FIRST_TICKET_PURCHASE_CAMPAIGN_USER_DATA_REQUEST = 0xD310 + GET_FIRST_TICKET_PURCHASE_CAMPAIGN_USER_DATA_RESPONSE = 0xD311 + GET_LINKED_SITE_REG_CAMPAIGN_USER_DATA_REQUEST = 0xD312 + GET_LINKED_SITE_REG_CAMPAIGN_USER_DATA_RESPONSE = 0xD313 + GET_HERO_LOG_UNIT_USER_DATA_LIST_REQUEST = 0xD400 + GET_HERO_LOG_UNIT_USER_DATA_LIST_RESPONSE = 0xD401 + GET_CHARA_UNIT_USER_DATA_LIST_REQUEST = 0xD402 + GET_CHARA_UNIT_USER_DATA_LIST_RESPONSE = 0xD403 + BNID_SERIAL_CODE_CHECK_REQUEST = 0xD404 + BNID_SERIAL_CODE_CHECK_RESPONSE = 0xD405 + BNID_SERIAL_CODE_ENTRY_BY_APPENDIX_CARD_REQUEST = 0xD406 + BNID_SERIAL_CODE_ENTRY_BY_APPENDIX_CARD_RESPONSE = 0xD407 + GET_M_PLAYER_RANKS_REQUEST = 0xD500 + GET_M_PLAYER_RANKS_RESPONSE = 0xD501 + GET_M_TITLES_REQUEST = 0xD502 + GET_M_TITLES_RESPONSE = 0xD503 + GET_M_FRAGMENTS_REQUEST = 0xD504 + GET_M_FRAGMENTS_RESPONSE = 0xD505 + GET_M_REWARD_TABLES_REQUEST = 0xD506 + GET_M_REWARD_TABLES_RESPONSE = 0xD507 + GET_M_REWARD_SETS_REQUEST = 0xD508 + GET_M_REWARD_SETS_RESPONSE = 0xD509 + GET_M_UNANALYZED_LOG_GRADES_REQUEST = 0xD50A + GET_M_UNANALYZED_LOG_GRADES_RESPONSE = 0xD50B + GET_M_APPOINT_LEADER_PARAMS_REQUEST = 0xD50C + GET_M_APPOINT_LEADER_PARAMS_RESPONSE = 0xD50D + GET_M_APPOINT_LEADER_EFFECTS_REQUEST = 0xD50E + GET_M_APPOINT_LEADER_EFFECTS_RESPONSE = 0xD50F + GET_M_APPOINT_LEADER_EFFECT_TYPES_REQUEST = 0xD510 + GET_M_APPOINT_LEADER_EFFECT_TYPES_RESPONSE = 0xD511 + GET_M_RARITIES_REQUEST = 0xD512 + GET_M_RARITIES_RESPONSE = 0xD513 + GET_M_COMPOSITION_EVENTS_REQUEST = 0xD514 + GET_M_COMPOSITION_EVENTS_RESPONSE = 0xD515 + GET_M_COMPOSITION_PARAMS_REQUEST = 0xD516 + GET_M_COMPOSITION_PARAMS_RESPONSE = 0xD517 + GET_M_GAME_PLAY_PRICES_REQUEST = 0xD518 + GET_M_GAME_PLAY_PRICES_RESPONSE = 0xD519 + GET_M_BUY_TICKETS_REQUEST = 0xD51A + GET_M_BUY_TICKETS_RESPONSE = 0xD51B + GET_M_TIPS_REQUEST = 0xD51C + GET_M_TIPS_RESPONSE = 0xD51D + GET_M_CAPS_REQUEST = 0xD51E + GET_M_CAPS_RESPONSE = 0xD51F + GET_M_HERO_LOG_REQUEST = 0xD520 + GET_M_HERO_LOG_RESPONSE = 0xD521 + GET_M_HERO_LOG_LEVELS_REQUEST = 0xD522 + GET_M_HERO_LOG_LEVELS_RESPONSE = 0xD523 + GET_M_HERO_LOG_ROLES_REQUEST = 0xD524 + GET_M_HERO_LOG_ROLES_RESPONSE = 0xD525 + GET_M_HERO_LOG_TRUST_RANKS_REQUEST = 0xD526 + GET_M_HERO_LOG_TRUST_RANKS_RESPONSE = 0xD527 + GET_M_CHARAS_REQUEST = 0xD528 + GET_M_CHARAS_RESPONSE = 0xD529 + GET_M_CHARA_FRIENDLY_RANKS_REQUEST = 0xD52A + GET_M_CHARA_FRIENDLY_RANKS_RESPONSE = 0xD52B + GET_M_EQUIPMENTS_REQUEST = 0xD52C + GET_M_EQUIPMENTS_RESPONSE = 0xD52D + GET_M_EQUIPMENT_LEVELS_REQUEST = 0xD52E + GET_M_EQUIPMENT_LEVELS_RESPONSE = 0xD52F + GET_M_WEAPON_TYPES_REQUEST = 0xD530 + GET_M_WEAPON_TYPES_RESPONSE = 0xD531 + GET_M_ITEMS_REQUEST = 0xD532 + GET_M_ITEMS_RESPONSE = 0xD533 + GET_M_ITEM_TYPES_REQUEST = 0xD534 + GET_M_ITEM_TYPES_RESPONSE = 0xD535 + GET_M_BUFF_ITEMS_REQUEST = 0xD536 + GET_M_BUFF_ITEMS_RESPONSE = 0xD537 + GET_M_ENEMIES_REQUEST = 0xD538 + GET_M_ENEMIES_RESPONSE = 0xD539 + GET_M_ENEMY_SETS_REQUEST = 0xD53A + GET_M_ENEMY_SETS_RESPONSE = 0xD53B + GET_M_ENEMY_KINDS_REQUEST = 0xD53C + GET_M_ENEMY_KINDS_RESPONSE = 0xD53D + GET_M_ENEMY_CATEGORIES_REQUEST = 0xD53E + GET_M_ENEMY_CATEGORIES_RESPONSE = 0xD53F + GET_M_UNITS_REQUEST = 0xD540 + GET_M_UNITS_RESPONSE = 0xD541 + GET_M_UNIT_GIMMICKS_REQUEST = 0xD542 + GET_M_UNIT_GIMMICKS_RESPONSE = 0xD543 + GET_M_UNIT_COLLISIONS_REQUEST = 0xD544 + GET_M_UNIT_COLLISIONS_RESPONSE = 0xD545 + GET_M_UNIT_POWERS_REQUEST = 0xD546 + GET_M_UNIT_POWERS_RESPONSE = 0xD547 + GET_M_GIMMICK_ATTACKS_REQUEST = 0xD548 + GET_M_GIMMICK_ATTACKS_RESPONSE = 0xD549 + GET_M_CHARA_ATTACKS_REQUEST = 0xD54A + GET_M_CHARA_ATTACKS_RESPONSE = 0xD54B + GET_M_BOSS_ATTACKS_REQUEST = 0xD54C + GET_M_BOSS_ATTACKS_RESPONSE = 0xD54D + GET_M_MONSTER_ATTACKS_REQUEST = 0xD54E + GET_M_MONSTER_ATTACKS_RESPONSE = 0xD54F + GET_M_MONSTER_ACTIONS_REQUEST = 0xD550 + GET_M_MONSTER_ACTIONS_RESPONSE = 0xD551 + GET_M_PROPERTIES_REQUEST = 0xD552 + GET_M_PROPERTIES_RESPONSE = 0xD553 + GET_M_PROPERTY_TABLES_REQUEST = 0xD554 + GET_M_PROPERTY_TABLES_RESPONSE = 0xD555 + GET_M_PROPERTY_TYPES_REQUEST = 0xD556 + GET_M_PROPERTY_TYPES_RESPONSE = 0xD557 + GET_M_SKILLS_REQUEST = 0xD558 + GET_M_SKILLS_RESPONSE = 0xD559 + GET_M_SKILL_TABLES_REQUEST = 0xD55A + GET_M_SKILL_TABLES_RESPONSE = 0xD55B + GET_M_SKILL_LEVELS_REQUEST = 0xD55C + GET_M_SKILL_LEVELS_RESPONSE = 0xD55D + GET_M_AWAKENINGS_REQUEST = 0xD55E + GET_M_AWAKENINGS_RESPONSE = 0xD55F + GET_M_SYNCHRO_SKILLS_REQUEST = 0xD560 + GET_M_SYNCHRO_SKILLS_RESPONSE = 0xD561 + GET_M_SOUND_SKILL_CUT_IN_VOICES_REQUEST = 0xD562 + GET_M_SOUND_SKILL_CUT_IN_VOICES_RESPONSE = 0xD563 + GET_M_QUEST_SCENES_REQUEST = 0xD564 + GET_M_QUEST_SCENES_RESPONSE = 0xD565 + GET_M_QUEST_EXIST_UNITS_REQUEST = 0xD566 + GET_M_QUEST_EXIST_UNITS_RESPONSE = 0xD567 + GET_M_QUEST_EPISODE_APPEND_REWARDS_REQUEST = 0xD568 + GET_M_QUEST_EPISODE_APPEND_REWARDS_RESPONSE = 0xD569 + GET_M_SIDE_QUESTS_REQUEST = 0xD56A + GET_M_SIDE_QUESTS_RESPONSE = 0xD56B + GET_M_EPISODES_REQUEST = 0xD56C + GET_M_EPISODES_RESPONSE = 0xD56D + GET_M_EPISODE_CHAPTERS_REQUEST = 0xD56E + GET_M_EPISODE_CHAPTERS_RESPONSE = 0xD56F + GET_M_EPISODE_PARTS_REQUEST = 0xD570 + GET_M_EPISODE_PARTS_RESPONSE = 0xD571 + GET_M_TRIAL_TOWERS_REQUEST = 0xD572 + GET_M_TRIAL_TOWERS_RESPONSE = 0xD573 + GET_M_EX_TOWERS_REQUEST = 0xD574 + GET_M_EX_TOWERS_RESPONSE = 0xD575 + GET_M_EX_TOWER_QUESTS_REQUEST = 0xD576 + GET_M_EX_TOWER_QUESTS_RESPONSE = 0xD577 + GET_M_MENU_DISPLAY_ENEMIES_REQUEST = 0xD578 + GET_M_MENU_DISPLAY_ENEMIES_RESPONSE = 0xD579 + GET_M_MISSIONS_REQUEST = 0xD57A + GET_M_MISSIONS_RESPONSE = 0xD57B + GET_M_MISSION_TABLES_REQUEST = 0xD57C + GET_M_MISSION_TABLES_RESPONSE = 0xD57D + GET_M_MISSION_DIFFICULTIES_REQUEST = 0xD57E + GET_M_MISSION_DIFFICULTIES_RESPONSE = 0xD57F + GET_M_BATTLE_CAMERAS_REQUEST = 0xD580 + GET_M_BATTLE_CAMERAS_RESPONSE = 0xD581 + GET_M_CHAT_MAIN_STORIES_REQUEST = 0xD582 + GET_M_CHAT_MAIN_STORIES_RESPONSE = 0xD583 + GET_M_CHAT_SIDE_STORIES_REQUEST = 0xD584 + GET_M_CHAT_SIDE_STORIES_RESPONSE = 0xD585 + GET_M_CHAT_EVENT_STORIES_REQUEST = 0xD586 + GET_M_CHAT_EVENT_STORIES_RESPONSE = 0xD587 + GET_M_NAVIGATOR_CHARAS_REQUEST = 0xD588 + GET_M_NAVIGATOR_CHARAS_RESPONSE = 0xD589 + GET_M_NAVIGATOR_COMMENTS_REQUEST = 0xD58A + GET_M_NAVIGATOR_COMMENTS_RESPONSE = 0xD58B + GET_M_EX_BONUS_TABLES_REQUEST = 0xD58C + GET_M_EX_BONUS_TABLES_RESPONSE = 0xD58D + GET_M_EX_BONUS_CONDITIONS_REQUEST = 0xD58E + GET_M_EX_BONUS_CONDITIONS_RESPONSE = 0xD58F + GET_M_QUEST_RARE_DROPS_REQUEST = 0xD590 + GET_M_QUEST_RARE_DROPS_RESPONSE = 0xD591 + GET_M_QUEST_SPECIAL_RARE_DROP_SETTINGS_REQUEST = 0xD592 + GET_M_QUEST_SPECIAL_RARE_DROP_SETTINGS_RESPONSE = 0xD593 + GET_M_QUEST_SPECIAL_RARE_DROPS_REQUEST = 0xD594 + GET_M_QUEST_SPECIAL_RARE_DROPS_RESPONSE = 0xD595 + GET_M_QUEST_TUTORIALS_REQUEST = 0xD596 + GET_M_QUEST_TUTORIALS_RESPONSE = 0xD597 + GET_M_QUEST_PLAYER_TRACE_TABLES_REQUEST = 0xD598 + GET_M_QUEST_PLAYER_TRACE_TABLES_RESPONSE = 0xD599 + GET_M_QUEST_STILLS_REQUEST = 0xD59A + GET_M_QUEST_STILLS_RESPONSE = 0xD59B + GET_M_GASHAS_REQUEST = 0xD59C + GET_M_GASHAS_RESPONSE = 0xD59D + GET_M_GASHA_HEADERS_REQUEST = 0xD59E + GET_M_GASHA_HEADERS_RESPONSE = 0xD59F + GET_M_GASHA_LOTTERY_RARITIES_REQUEST = 0xD5A0 + GET_M_GASHA_LOTTERY_RARITIES_RESPONSE = 0xD5A1 + GET_M_GASHA_PRIZES_REQUEST = 0xD5A2 + GET_M_GASHA_PRIZES_RESPONSE = 0xD5A3 + GET_M_COMEBACK_EVENTS_REQUEST = 0xD5A4 + GET_M_COMEBACK_EVENTS_RESPONSE = 0xD5A5 + GET_M_AD_BANNERS_REQUEST = 0xD5A6 + GET_M_AD_BANNERS_RESPONSE = 0xD5A7 + GET_M_EVENTS_REQUEST = 0xD5A8 + GET_M_EVENTS_RESPONSE = 0xD5A9 + GET_M_TREASURE_HUNTS_REQUEST = 0xD5AA + GET_M_TREASURE_HUNTS_RESPONSE = 0xD5AB + GET_M_TREASURE_HUNT_WHOLE_TASKS_REQUEST = 0xD5AC + GET_M_TREASURE_HUNT_WHOLE_TASKS_RESPONSE = 0xD5AD + GET_M_TREASURE_HUNT_INDIVIDUAL_TASKS_REQUEST = 0xD5AE + GET_M_TREASURE_HUNT_INDIVIDUAL_TASKS_RESPONSE = 0xD5AF + GET_M_TREASURE_HUNT_SPECIAL_EFFECTS_REQUEST = 0xD5B0 + GET_M_TREASURE_HUNT_SPECIAL_EFFECTS_RESPONSE = 0xD5B1 + GET_M_TREASURE_HUNT_EVENT_POINT_REWARD_COMMON_REWARDS_REQUEST = 0xD5B2 + GET_M_TREASURE_HUNT_EVENT_POINT_REWARD_COMMON_REWARDS_RESPONSE = 0xD5B3 + GET_M_TREASURE_HUNT_EVENT_POINT_REWARD_TITLES_REQUEST = 0xD5B4 + GET_M_TREASURE_HUNT_EVENT_POINT_REWARD_TITLES_RESPONSE = 0xD5B5 + GET_M_TREASURE_HUNT_TASK_TEXTS_REQUEST = 0xD5B6 + GET_M_TREASURE_HUNT_TASK_TEXTS_RESPONSE = 0xD5B7 + GET_M_BNID_SERIAL_CODES_REQUEST = 0xD5B8 + GET_M_BNID_SERIAL_CODES_RESPONSE = 0xD5B9 + GET_M_BNID_SERIAL_CODE_REWARDS_REQUEST = 0xD5BA + GET_M_BNID_SERIAL_CODE_REWARDS_RESPONSE = 0xD5BB + GET_M_SUPPORT_LOG_REQUEST = 0xD5BC + GET_M_SUPPORT_LOG_RESPONSE = 0xD5BD + GET_M_SUPPORT_LOG_TYPES_REQUEST = 0xD5BE + GET_M_SUPPORT_LOG_TYPES_RESPONSE = 0xD5BF + GET_M_EPISODE_APPENDS_REQUEST = 0xD5C0 + GET_M_EPISODE_APPENDS_RESPONSE = 0xD5C1 + GET_M_QUEST_DEFRAG_MATCH_QUESTS_REQUEST = 0xD5C2 + GET_M_QUEST_DEFRAG_MATCH_QUESTS_RESPONSE = 0xD5C3 + GET_M_QUEST_DEFRAG_MATCH_QUEST_BOSS_TABLES_REQUEST = 0xD5C4 + GET_M_QUEST_DEFRAG_MATCH_QUEST_BOSS_TABLES_RESPONSE = 0xD5C5 + GET_M_DEFRAG_MATCHES_REQUEST = 0xD5C6 + GET_M_DEFRAG_MATCHES_RESPONSE = 0xD5C7 + GET_M_DEFRAG_MATCH_SEED_REQUEST = 0xD5C8 + GET_M_DEFRAG_MATCH_SEED_RESPONSE = 0xD5C9 + GET_M_DEFRAG_MATCH_SPECIAL_EFFECTS_REQUEST = 0xD5CA + GET_M_DEFRAG_MATCH_SPECIAL_EFFECTS_RESPONSE = 0xD5CB + GET_M_DEFRAG_MATCH_GRADES_REQUEST = 0xD5CC + GET_M_DEFRAG_MATCH_GRADES_RESPONSE = 0xD5CD + GET_M_DEFRAG_MATCH_CPU_UNITS_REQUEST = 0xD5CE + GET_M_DEFRAG_MATCH_CPU_UNITS_RESPONSE = 0xD5CF + GET_M_DEFRAG_MATCH_CPU_SUPPORT_LOGS_REQUEST = 0xD5D0 + GET_M_DEFRAG_MATCH_CPU_SUPPORT_LOGS_RESPONSE = 0xD5D1 + GET_M_DEFRAG_MATCH_PERIOD_BONUSES_REQUEST = 0xD5D2 + GET_M_DEFRAG_MATCH_PERIOD_BONUSES_RESPONSE = 0xD5D3 + GET_M_DEFRAG_MATCH_RANDOM_BONUS_TABLES_REQUEST = 0xD5D4 + GET_M_DEFRAG_MATCH_RANDOM_BONUS_TABLES_RESPONSE = 0xD5D5 + GET_M_DEFRAG_MATCH_RANDOM_BONUS_CONDITIONS_REQUEST = 0xD5D6 + GET_M_DEFRAG_MATCH_RANDOM_BONUS_CONDITIONS_RESPONSE = 0xD5D7 + GET_M_DEFRAG_MATCH_RARE_DROPS_REQUEST = 0xD5D8 + GET_M_DEFRAG_MATCH_RARE_DROPS_RESPONSE = 0xD5D9 + GET_M_YUI_MEDAL_SHOPS_REQUEST = 0xD5DA + GET_M_YUI_MEDAL_SHOPS_RESPONSE = 0xD5DB + GET_M_YUI_MEDAL_SHOP_ITEMS_REQUEST = 0xD5DC + GET_M_YUI_MEDAL_SHOP_ITEMS_RESPONSE = 0xD5DD + GET_M_EVENT_SCENES_REQUEST = 0xD5DE + GET_M_EVENT_SCENES_RESPONSE = 0xD5DF + GET_M_GENERIC_CAMPAIGN_PERIODS_REQUEST = 0xD5E0 + GET_M_GENERIC_CAMPAIGN_PERIODS_RESPONSE = 0xD5E1 + GET_M_BEGINNER_MISSIONS_REQUEST = 0xD5E2 + GET_M_BEGINNER_MISSIONS_RESPONSE = 0xD5E3 + GET_M_BEGINNER_MISSION_CONDITIONS_REQUEST = 0xD5E4 + GET_M_BEGINNER_MISSION_CONDITIONS_RESPONSE = 0xD5E5 + GET_M_BEGINNER_MISSION_REWARDS_REQUEST = 0xD5E6 + GET_M_BEGINNER_MISSION_REWARDS_RESPONSE = 0xD5E7 + GET_M_BEGINNER_MISSION_SEAT_CONDITIONS_REQUEST = 0xD5E8 + GET_M_BEGINNER_MISSION_SEAT_CONDITIONS_RESPONSE = 0xD5E9 + GET_M_BEGINNER_MISSION_SEAT_REWARDS_REQUEST = 0xD5EA + GET_M_BEGINNER_MISSION_SEAT_REWARDS_RESPONSE = 0xD5EB + GET_M_EVENT_ITEMS_REQUEST = 0xD5EC + GET_M_EVENT_ITEMS_RESPONSE = 0xD5ED + GET_M_EVENT_MONSTERS_REQUEST = 0xD5EE + GET_M_EVENT_MONSTERS_RESPONSE = 0xD5EF + GET_M_YUI_MEDAL_BONUSES_REQUEST = 0xD5F0 + GET_M_YUI_MEDAL_BONUSES_RESPONSE = 0xD5F1 + GET_M_YUI_MEDAL_BONUS_CONDITIONS_REQUEST = 0xD5F2 + GET_M_YUI_MEDAL_BONUS_CONDITIONS_RESPONSE = 0xD5F3 + GET_M_GASHA_MEDALS_REQUEST = 0xD5F4 + GET_M_GASHA_MEDALS_RESPONSE = 0xD5F5 + GET_M_GASHA_MEDAL_TYPES_REQUEST = 0xD5F6 + GET_M_GASHA_MEDAL_TYPES_RESPONSE = 0xD5F7 + GET_M_GASHA_MEDAL_SETTINGS_REQUEST = 0xD5F8 + GET_M_GASHA_MEDAL_SETTINGS_RESPONSE = 0xD5F9 + GET_M_GASHA_MEDAL_BONUSES_REQUEST = 0xD5FA + GET_M_GASHA_MEDAL_BONUSES_RESPONSE = 0xD5FB + GET_M_GASHA_MEDAL_SHOPS_REQUEST = 0xD5FC + GET_M_GASHA_MEDAL_SHOPS_RESPONSE = 0xD5FD + GET_M_GASHA_MEDAL_SHOP_ITEMS_REQUEST = 0xD5FE + GET_M_GASHA_MEDAL_SHOP_ITEMS_RESPONSE = 0xD5FF + GET_M_RES_EARN_CAMPAIGN_APPLICATIONS_REQUEST = 0xD600 + GET_M_RES_EARN_CAMPAIGN_APPLICATIONS_RESPONSE = 0xD601 + GET_M_RES_EARN_CAMPAIGN_APPLICATION_PRODUCTS_REQUEST = 0xD602 + GET_M_RES_EARN_CAMPAIGN_APPLICATION_PRODUCTS_RESPONSE = 0xD603 + GET_M_RES_EARN_CAMPAIGN_SHOPS_REQUEST = 0xD604 + GET_M_RES_EARN_CAMPAIGN_SHOPS_RESPONSE = 0xD605 + GET_M_RES_EARN_CAMPAIGN_SHOP_ITEMS_REQUEST = 0xD606 + GET_M_RES_EARN_CAMPAIGN_SHOP_ITEMS_RESPONSE = 0xD607 + GET_M_PAYING_YUI_MEDAL_BONUSES_REQUEST = 0xD608 + GET_M_PAYING_YUI_MEDAL_BONUSES_RESPONSE = 0xD609 + GET_M_AC_LOGIN_BONUSES_REQUEST = 0xD60A + GET_M_AC_LOGIN_BONUSES_RESPONSE = 0xD60B + GET_M_PLAY_CAMPAIGNS_REQUEST = 0xD60C + GET_M_PLAY_CAMPAIGNS_RESPONSE = 0xD60D + GET_M_PLAY_CAMPAIGN_REWARDS_REQUEST = 0xD60E + GET_M_PLAY_CAMPAIGN_REWARDS_RESPONSE = 0xD60F + GET_M_GASHA_FREE_CAMPAIGNS_REQUEST = 0xD610 + GET_M_GASHA_FREE_CAMPAIGNS_RESPONSE = 0xD611 + GET_M_QUEST_DROP_BOOST_CAMPAIGNS_REQUEST = 0xD612 + GET_M_QUEST_DROP_BOOST_CAMPAIGNS_RESPONSE = 0xD613 + GET_M_FIRST_TICKET_PURCHASE_CAMPAIGNS_REQUEST = 0xD614 + GET_M_FIRST_TICKET_PURCHASE_CAMPAIGNS_RESPONSE = 0xD615 + GET_M_LINKED_SITE_REG_CAMPAIGNS_REQUEST = 0xD616 + GET_M_LINKED_SITE_REG_CAMPAIGNS_RESPONSE = 0xD617 + GET_M_LINKED_SITE_REG_CAMPAIGN_REWARDS_REQUEST = 0xD618 + GET_M_LINKED_SITE_REG_CAMPAIGN_REWARDS_RESPONSE = 0xD619 diff --git a/titles/sao/data/1/AcLoginBonuses.csv b/titles/sao/data/1/AcLoginBonuses.csv new file mode 100644 index 0000000..166c5b2 Binary files /dev/null and b/titles/sao/data/1/AcLoginBonuses.csv differ diff --git a/titles/sao/data/1/AdBanners.csv b/titles/sao/data/1/AdBanners.csv new file mode 100644 index 0000000..52400ff Binary files /dev/null and b/titles/sao/data/1/AdBanners.csv differ diff --git a/titles/sao/data/1/AppointLeaderEffect.csv b/titles/sao/data/1/AppointLeaderEffect.csv new file mode 100644 index 0000000..d7079fd Binary files /dev/null and b/titles/sao/data/1/AppointLeaderEffect.csv differ diff --git a/titles/sao/data/1/AppointLeaderEffectType.csv b/titles/sao/data/1/AppointLeaderEffectType.csv new file mode 100644 index 0000000..754a94e Binary files /dev/null and b/titles/sao/data/1/AppointLeaderEffectType.csv differ diff --git a/titles/sao/data/1/AppointLeaderParam.csv b/titles/sao/data/1/AppointLeaderParam.csv new file mode 100644 index 0000000..173f312 Binary files /dev/null and b/titles/sao/data/1/AppointLeaderParam.csv differ diff --git a/titles/sao/data/1/Awakening.csv b/titles/sao/data/1/Awakening.csv new file mode 100644 index 0000000..05d1bd0 Binary files /dev/null and b/titles/sao/data/1/Awakening.csv differ diff --git a/titles/sao/data/1/BattleCamera.csv b/titles/sao/data/1/BattleCamera.csv new file mode 100644 index 0000000..d4410da Binary files /dev/null and b/titles/sao/data/1/BattleCamera.csv differ diff --git a/titles/sao/data/1/BeginnerMissionConditions.csv b/titles/sao/data/1/BeginnerMissionConditions.csv new file mode 100644 index 0000000..7b7c9af Binary files /dev/null and b/titles/sao/data/1/BeginnerMissionConditions.csv differ diff --git a/titles/sao/data/1/BeginnerMissionRewards.csv b/titles/sao/data/1/BeginnerMissionRewards.csv new file mode 100644 index 0000000..8e24ce5 Binary files /dev/null and b/titles/sao/data/1/BeginnerMissionRewards.csv differ diff --git a/titles/sao/data/1/BeginnerMissionSeatConditions.csv b/titles/sao/data/1/BeginnerMissionSeatConditions.csv new file mode 100644 index 0000000..ad97ac7 Binary files /dev/null and b/titles/sao/data/1/BeginnerMissionSeatConditions.csv differ diff --git a/titles/sao/data/1/BeginnerMissionSeatRewards.csv b/titles/sao/data/1/BeginnerMissionSeatRewards.csv new file mode 100644 index 0000000..c306a39 Binary files /dev/null and b/titles/sao/data/1/BeginnerMissionSeatRewards.csv differ diff --git a/titles/sao/data/1/BeginnerMissions.csv b/titles/sao/data/1/BeginnerMissions.csv new file mode 100644 index 0000000..56cbf19 Binary files /dev/null and b/titles/sao/data/1/BeginnerMissions.csv differ diff --git a/titles/sao/data/1/BnidSerialCodeRewards.csv b/titles/sao/data/1/BnidSerialCodeRewards.csv new file mode 100644 index 0000000..556883d Binary files /dev/null and b/titles/sao/data/1/BnidSerialCodeRewards.csv differ diff --git a/titles/sao/data/1/BnidSerialCodes.csv b/titles/sao/data/1/BnidSerialCodes.csv new file mode 100644 index 0000000..5eff1e9 Binary files /dev/null and b/titles/sao/data/1/BnidSerialCodes.csv differ diff --git a/titles/sao/data/1/BossAttack.csv b/titles/sao/data/1/BossAttack.csv new file mode 100644 index 0000000..2ae2572 Binary files /dev/null and b/titles/sao/data/1/BossAttack.csv differ diff --git a/titles/sao/data/1/BuffItem.csv b/titles/sao/data/1/BuffItem.csv new file mode 100644 index 0000000..b4f3fcd Binary files /dev/null and b/titles/sao/data/1/BuffItem.csv differ diff --git a/titles/sao/data/1/BuyTicket.csv b/titles/sao/data/1/BuyTicket.csv new file mode 100644 index 0000000..58f0c48 Binary files /dev/null and b/titles/sao/data/1/BuyTicket.csv differ diff --git a/titles/sao/data/1/Cap.csv b/titles/sao/data/1/Cap.csv new file mode 100644 index 0000000..2d57742 Binary files /dev/null and b/titles/sao/data/1/Cap.csv differ diff --git a/titles/sao/data/1/Chara.csv b/titles/sao/data/1/Chara.csv new file mode 100644 index 0000000..00ae23e Binary files /dev/null and b/titles/sao/data/1/Chara.csv differ diff --git a/titles/sao/data/1/CharaAttack.csv b/titles/sao/data/1/CharaAttack.csv new file mode 100644 index 0000000..95d3fe4 Binary files /dev/null and b/titles/sao/data/1/CharaAttack.csv differ diff --git a/titles/sao/data/1/CharaComment.csv b/titles/sao/data/1/CharaComment.csv new file mode 100644 index 0000000..2836383 Binary files /dev/null and b/titles/sao/data/1/CharaComment.csv differ diff --git a/titles/sao/data/1/CharaFriendlyRank.csv b/titles/sao/data/1/CharaFriendlyRank.csv new file mode 100644 index 0000000..0e39ed1 Binary files /dev/null and b/titles/sao/data/1/CharaFriendlyRank.csv differ diff --git a/titles/sao/data/1/ChatEventStory.csv b/titles/sao/data/1/ChatEventStory.csv new file mode 100644 index 0000000..3616cc0 Binary files /dev/null and b/titles/sao/data/1/ChatEventStory.csv differ diff --git a/titles/sao/data/1/ChatMainStory.csv b/titles/sao/data/1/ChatMainStory.csv new file mode 100644 index 0000000..542d3b6 Binary files /dev/null and b/titles/sao/data/1/ChatMainStory.csv differ diff --git a/titles/sao/data/1/ChatSideStory.csv b/titles/sao/data/1/ChatSideStory.csv new file mode 100644 index 0000000..e6ad4f0 Binary files /dev/null and b/titles/sao/data/1/ChatSideStory.csv differ diff --git a/titles/sao/data/1/ComebackEvent.csv b/titles/sao/data/1/ComebackEvent.csv new file mode 100644 index 0000000..2c8e775 Binary files /dev/null and b/titles/sao/data/1/ComebackEvent.csv differ diff --git a/titles/sao/data/1/CompositionEvent.csv b/titles/sao/data/1/CompositionEvent.csv new file mode 100644 index 0000000..a221ad5 Binary files /dev/null and b/titles/sao/data/1/CompositionEvent.csv differ diff --git a/titles/sao/data/1/CompositionParam.csv b/titles/sao/data/1/CompositionParam.csv new file mode 100644 index 0000000..7314322 Binary files /dev/null and b/titles/sao/data/1/CompositionParam.csv differ diff --git a/titles/sao/data/1/CostumeType.csv b/titles/sao/data/1/CostumeType.csv new file mode 100644 index 0000000..bd1ce89 Binary files /dev/null and b/titles/sao/data/1/CostumeType.csv differ diff --git a/titles/sao/data/1/DebugValidator.csv b/titles/sao/data/1/DebugValidator.csv new file mode 100644 index 0000000..edaaf66 Binary files /dev/null and b/titles/sao/data/1/DebugValidator.csv differ diff --git a/titles/sao/data/1/DefragMatchAIPattern.csv b/titles/sao/data/1/DefragMatchAIPattern.csv new file mode 100644 index 0000000..862581a Binary files /dev/null and b/titles/sao/data/1/DefragMatchAIPattern.csv differ diff --git a/titles/sao/data/1/DefragMatchBossTable.csv b/titles/sao/data/1/DefragMatchBossTable.csv new file mode 100644 index 0000000..d5b19ee Binary files /dev/null and b/titles/sao/data/1/DefragMatchBossTable.csv differ diff --git a/titles/sao/data/1/DefragMatchChat.csv b/titles/sao/data/1/DefragMatchChat.csv new file mode 100644 index 0000000..6aa812e Binary files /dev/null and b/titles/sao/data/1/DefragMatchChat.csv differ diff --git a/titles/sao/data/1/DefragMatchCpuSupportLogs.csv b/titles/sao/data/1/DefragMatchCpuSupportLogs.csv new file mode 100644 index 0000000..62746e2 Binary files /dev/null and b/titles/sao/data/1/DefragMatchCpuSupportLogs.csv differ diff --git a/titles/sao/data/1/DefragMatchCpuUnits.csv b/titles/sao/data/1/DefragMatchCpuUnits.csv new file mode 100644 index 0000000..c035b67 Binary files /dev/null and b/titles/sao/data/1/DefragMatchCpuUnits.csv differ diff --git a/titles/sao/data/1/DefragMatchGrade.csv b/titles/sao/data/1/DefragMatchGrade.csv new file mode 100644 index 0000000..b81e7bf Binary files /dev/null and b/titles/sao/data/1/DefragMatchGrade.csv differ diff --git a/titles/sao/data/1/DefragMatchPeriodBonuses.csv b/titles/sao/data/1/DefragMatchPeriodBonuses.csv new file mode 100644 index 0000000..eb348f5 Binary files /dev/null and b/titles/sao/data/1/DefragMatchPeriodBonuses.csv differ diff --git a/titles/sao/data/1/DefragMatchQuest.csv b/titles/sao/data/1/DefragMatchQuest.csv new file mode 100644 index 0000000..89cabc0 Binary files /dev/null and b/titles/sao/data/1/DefragMatchQuest.csv differ diff --git a/titles/sao/data/1/DefragMatchRandomBonusConditions.csv b/titles/sao/data/1/DefragMatchRandomBonusConditions.csv new file mode 100644 index 0000000..47c2e01 Binary files /dev/null and b/titles/sao/data/1/DefragMatchRandomBonusConditions.csv differ diff --git a/titles/sao/data/1/DefragMatchRandomBonusTables.csv b/titles/sao/data/1/DefragMatchRandomBonusTables.csv new file mode 100644 index 0000000..6c8bf15 Binary files /dev/null and b/titles/sao/data/1/DefragMatchRandomBonusTables.csv differ diff --git a/titles/sao/data/1/DefragMatchRareDrops.csv b/titles/sao/data/1/DefragMatchRareDrops.csv new file mode 100644 index 0000000..50ec74d Binary files /dev/null and b/titles/sao/data/1/DefragMatchRareDrops.csv differ diff --git a/titles/sao/data/1/DefragMatchSeed.csv b/titles/sao/data/1/DefragMatchSeed.csv new file mode 100644 index 0000000..a5903cb Binary files /dev/null and b/titles/sao/data/1/DefragMatchSeed.csv differ diff --git a/titles/sao/data/1/DefragMatchSpecialEffects.csv b/titles/sao/data/1/DefragMatchSpecialEffects.csv new file mode 100644 index 0000000..48244f2 Binary files /dev/null and b/titles/sao/data/1/DefragMatchSpecialEffects.csv differ diff --git a/titles/sao/data/1/DefragMatchs.csv b/titles/sao/data/1/DefragMatchs.csv new file mode 100644 index 0000000..5115b35 Binary files /dev/null and b/titles/sao/data/1/DefragMatchs.csv differ diff --git a/titles/sao/data/1/Enemy.csv b/titles/sao/data/1/Enemy.csv new file mode 100644 index 0000000..8f5323f Binary files /dev/null and b/titles/sao/data/1/Enemy.csv differ diff --git a/titles/sao/data/1/EnemyKind.csv b/titles/sao/data/1/EnemyKind.csv new file mode 100644 index 0000000..e969dbf Binary files /dev/null and b/titles/sao/data/1/EnemyKind.csv differ diff --git a/titles/sao/data/1/EnemySet.csv b/titles/sao/data/1/EnemySet.csv new file mode 100644 index 0000000..771ed20 Binary files /dev/null and b/titles/sao/data/1/EnemySet.csv differ diff --git a/titles/sao/data/1/Episode.csv b/titles/sao/data/1/Episode.csv new file mode 100644 index 0000000..8cb8c6d Binary files /dev/null and b/titles/sao/data/1/Episode.csv differ diff --git a/titles/sao/data/1/EpisodeAppends.csv b/titles/sao/data/1/EpisodeAppends.csv new file mode 100644 index 0000000..665d16f Binary files /dev/null and b/titles/sao/data/1/EpisodeAppends.csv differ diff --git a/titles/sao/data/1/EpisodeChapter.csv b/titles/sao/data/1/EpisodeChapter.csv new file mode 100644 index 0000000..834419f Binary files /dev/null and b/titles/sao/data/1/EpisodeChapter.csv differ diff --git a/titles/sao/data/1/EpisodePart.csv b/titles/sao/data/1/EpisodePart.csv new file mode 100644 index 0000000..97134fa Binary files /dev/null and b/titles/sao/data/1/EpisodePart.csv differ diff --git a/titles/sao/data/Equipment.csv b/titles/sao/data/1/Equipment.csv similarity index 86% rename from titles/sao/data/Equipment.csv rename to titles/sao/data/1/Equipment.csv index a7fd263..96fb7cb 100644 Binary files a/titles/sao/data/Equipment.csv and b/titles/sao/data/1/Equipment.csv differ diff --git a/titles/sao/data/1/EquipmentLevel.csv b/titles/sao/data/1/EquipmentLevel.csv new file mode 100644 index 0000000..68f4e43 Binary files /dev/null and b/titles/sao/data/1/EquipmentLevel.csv differ diff --git a/titles/sao/data/1/Event.csv b/titles/sao/data/1/Event.csv new file mode 100644 index 0000000..18a37ed Binary files /dev/null and b/titles/sao/data/1/Event.csv differ diff --git a/titles/sao/data/1/EventItems.csv b/titles/sao/data/1/EventItems.csv new file mode 100644 index 0000000..3842ab5 Binary files /dev/null and b/titles/sao/data/1/EventItems.csv differ diff --git a/titles/sao/data/1/EventMonsters.csv b/titles/sao/data/1/EventMonsters.csv new file mode 100644 index 0000000..d756ff5 Binary files /dev/null and b/titles/sao/data/1/EventMonsters.csv differ diff --git a/titles/sao/data/1/EventScenes.csv b/titles/sao/data/1/EventScenes.csv new file mode 100644 index 0000000..d5cde61 Binary files /dev/null and b/titles/sao/data/1/EventScenes.csv differ diff --git a/titles/sao/data/1/ExBonusCondition.csv b/titles/sao/data/1/ExBonusCondition.csv new file mode 100644 index 0000000..d6f0f9a Binary files /dev/null and b/titles/sao/data/1/ExBonusCondition.csv differ diff --git a/titles/sao/data/1/ExBonusTable.csv b/titles/sao/data/1/ExBonusTable.csv new file mode 100644 index 0000000..d85ffac Binary files /dev/null and b/titles/sao/data/1/ExBonusTable.csv differ diff --git a/titles/sao/data/1/ExTowerQuests.csv b/titles/sao/data/1/ExTowerQuests.csv new file mode 100644 index 0000000..0b8a944 Binary files /dev/null and b/titles/sao/data/1/ExTowerQuests.csv differ diff --git a/titles/sao/data/1/ExTowers.csv b/titles/sao/data/1/ExTowers.csv new file mode 100644 index 0000000..7b371e9 Binary files /dev/null and b/titles/sao/data/1/ExTowers.csv differ diff --git a/titles/sao/data/1/FirstTicketPurchaseCampaigns.csv b/titles/sao/data/1/FirstTicketPurchaseCampaigns.csv new file mode 100644 index 0000000..21a19f6 Binary files /dev/null and b/titles/sao/data/1/FirstTicketPurchaseCampaigns.csv differ diff --git a/titles/sao/data/1/FixedFormChat.csv b/titles/sao/data/1/FixedFormChat.csv new file mode 100644 index 0000000..2433d02 Binary files /dev/null and b/titles/sao/data/1/FixedFormChat.csv differ diff --git a/titles/sao/data/1/FixedFormChatText.csv b/titles/sao/data/1/FixedFormChatText.csv new file mode 100644 index 0000000..72e3f1b Binary files /dev/null and b/titles/sao/data/1/FixedFormChatText.csv differ diff --git a/titles/sao/data/1/Fragment.csv b/titles/sao/data/1/Fragment.csv new file mode 100644 index 0000000..5273d23 Binary files /dev/null and b/titles/sao/data/1/Fragment.csv differ diff --git a/titles/sao/data/1/GamePlayPrice.csv b/titles/sao/data/1/GamePlayPrice.csv new file mode 100644 index 0000000..5104fe9 Binary files /dev/null and b/titles/sao/data/1/GamePlayPrice.csv differ diff --git a/titles/sao/data/1/GashaFreeCampaigns.csv b/titles/sao/data/1/GashaFreeCampaigns.csv new file mode 100644 index 0000000..145be89 Binary files /dev/null and b/titles/sao/data/1/GashaFreeCampaigns.csv differ diff --git a/titles/sao/data/1/GashaMedalBonuses.csv b/titles/sao/data/1/GashaMedalBonuses.csv new file mode 100644 index 0000000..3df4b2a Binary files /dev/null and b/titles/sao/data/1/GashaMedalBonuses.csv differ diff --git a/titles/sao/data/1/GashaMedalSettings.csv b/titles/sao/data/1/GashaMedalSettings.csv new file mode 100644 index 0000000..b4a7566 Binary files /dev/null and b/titles/sao/data/1/GashaMedalSettings.csv differ diff --git a/titles/sao/data/1/GashaMedalShopItems.csv b/titles/sao/data/1/GashaMedalShopItems.csv new file mode 100644 index 0000000..66a3bac Binary files /dev/null and b/titles/sao/data/1/GashaMedalShopItems.csv differ diff --git a/titles/sao/data/GashaMedalShops.csv b/titles/sao/data/1/GashaMedalShops.csv similarity index 65% rename from titles/sao/data/GashaMedalShops.csv rename to titles/sao/data/1/GashaMedalShops.csv index a4a78dc..b92e0b4 100644 Binary files a/titles/sao/data/GashaMedalShops.csv and b/titles/sao/data/1/GashaMedalShops.csv differ diff --git a/titles/sao/data/1/GashaMedalTypes.csv b/titles/sao/data/1/GashaMedalTypes.csv new file mode 100644 index 0000000..7e6d6fd Binary files /dev/null and b/titles/sao/data/1/GashaMedalTypes.csv differ diff --git a/titles/sao/data/1/GashaMedals.csv b/titles/sao/data/1/GashaMedals.csv new file mode 100644 index 0000000..d399fbc Binary files /dev/null and b/titles/sao/data/1/GashaMedals.csv differ diff --git a/titles/sao/data/1/GenericCampaignPeriods.csv b/titles/sao/data/1/GenericCampaignPeriods.csv new file mode 100644 index 0000000..839ceb2 Binary files /dev/null and b/titles/sao/data/1/GenericCampaignPeriods.csv differ diff --git a/titles/sao/data/1/GimmickAttack.csv b/titles/sao/data/1/GimmickAttack.csv new file mode 100644 index 0000000..e177454 Binary files /dev/null and b/titles/sao/data/1/GimmickAttack.csv differ diff --git a/titles/sao/data/HeroLog.csv b/titles/sao/data/1/HeroLog.csv similarity index 94% rename from titles/sao/data/HeroLog.csv rename to titles/sao/data/1/HeroLog.csv index 6fbacac..339cce3 100644 Binary files a/titles/sao/data/HeroLog.csv and b/titles/sao/data/1/HeroLog.csv differ diff --git a/titles/sao/data/1/HeroLogLevel.csv b/titles/sao/data/1/HeroLogLevel.csv new file mode 100644 index 0000000..4a9e8fd Binary files /dev/null and b/titles/sao/data/1/HeroLogLevel.csv differ diff --git a/titles/sao/data/1/HeroLogRole.csv b/titles/sao/data/1/HeroLogRole.csv new file mode 100644 index 0000000..4b75e29 Binary files /dev/null and b/titles/sao/data/1/HeroLogRole.csv differ diff --git a/titles/sao/data/1/HeroLogTrustRank.csv b/titles/sao/data/1/HeroLogTrustRank.csv new file mode 100644 index 0000000..cfc8e0d Binary files /dev/null and b/titles/sao/data/1/HeroLogTrustRank.csv differ diff --git a/titles/sao/data/Item.csv b/titles/sao/data/1/Item.csv similarity index 79% rename from titles/sao/data/Item.csv rename to titles/sao/data/1/Item.csv index fc780af..564bf8f 100644 Binary files a/titles/sao/data/Item.csv and b/titles/sao/data/1/Item.csv differ diff --git a/titles/sao/data/1/ItemType.csv b/titles/sao/data/1/ItemType.csv new file mode 100644 index 0000000..ada5092 Binary files /dev/null and b/titles/sao/data/1/ItemType.csv differ diff --git a/titles/sao/data/1/LinkedSiteRegCampaignRewards.csv b/titles/sao/data/1/LinkedSiteRegCampaignRewards.csv new file mode 100644 index 0000000..20b34b3 Binary files /dev/null and b/titles/sao/data/1/LinkedSiteRegCampaignRewards.csv differ diff --git a/titles/sao/data/1/LinkedSiteRegCampaigns.csv b/titles/sao/data/1/LinkedSiteRegCampaigns.csv new file mode 100644 index 0000000..745a86d Binary files /dev/null and b/titles/sao/data/1/LinkedSiteRegCampaigns.csv differ diff --git a/titles/sao/data/1/MenuDisplayEnemy.csv b/titles/sao/data/1/MenuDisplayEnemy.csv new file mode 100644 index 0000000..50db0f5 Binary files /dev/null and b/titles/sao/data/1/MenuDisplayEnemy.csv differ diff --git a/titles/sao/data/1/Mission.csv b/titles/sao/data/1/Mission.csv new file mode 100644 index 0000000..bacb114 Binary files /dev/null and b/titles/sao/data/1/Mission.csv differ diff --git a/titles/sao/data/1/MissionClearText.csv b/titles/sao/data/1/MissionClearText.csv new file mode 100644 index 0000000..316e1ee Binary files /dev/null and b/titles/sao/data/1/MissionClearText.csv differ diff --git a/titles/sao/data/1/MissionDifficulty.csv b/titles/sao/data/1/MissionDifficulty.csv new file mode 100644 index 0000000..9eebb74 Binary files /dev/null and b/titles/sao/data/1/MissionDifficulty.csv differ diff --git a/titles/sao/data/1/MissionTable.csv b/titles/sao/data/1/MissionTable.csv new file mode 100644 index 0000000..b399f22 Binary files /dev/null and b/titles/sao/data/1/MissionTable.csv differ diff --git a/titles/sao/data/1/MonsterAction.csv b/titles/sao/data/1/MonsterAction.csv new file mode 100644 index 0000000..052d917 Binary files /dev/null and b/titles/sao/data/1/MonsterAction.csv differ diff --git a/titles/sao/data/1/MonsterAttack.csv b/titles/sao/data/1/MonsterAttack.csv new file mode 100644 index 0000000..06f8838 Binary files /dev/null and b/titles/sao/data/1/MonsterAttack.csv differ diff --git a/titles/sao/data/1/NavigatorChara.csv b/titles/sao/data/1/NavigatorChara.csv new file mode 100644 index 0000000..e1a42ac Binary files /dev/null and b/titles/sao/data/1/NavigatorChara.csv differ diff --git a/titles/sao/data/1/NavigatorComment.csv b/titles/sao/data/1/NavigatorComment.csv new file mode 100644 index 0000000..5651a72 Binary files /dev/null and b/titles/sao/data/1/NavigatorComment.csv differ diff --git a/titles/sao/data/1/PayingYuiMedalBonuses.csv b/titles/sao/data/1/PayingYuiMedalBonuses.csv new file mode 100644 index 0000000..91a77c4 Binary files /dev/null and b/titles/sao/data/1/PayingYuiMedalBonuses.csv differ diff --git a/titles/sao/data/1/PlayCampaignRewards.csv b/titles/sao/data/1/PlayCampaignRewards.csv new file mode 100644 index 0000000..627fbee Binary files /dev/null and b/titles/sao/data/1/PlayCampaignRewards.csv differ diff --git a/titles/sao/data/1/PlayCampaigns.csv b/titles/sao/data/1/PlayCampaigns.csv new file mode 100644 index 0000000..96ec39c Binary files /dev/null and b/titles/sao/data/1/PlayCampaigns.csv differ diff --git a/titles/sao/data/1/PlayerRank.csv b/titles/sao/data/1/PlayerRank.csv new file mode 100644 index 0000000..fe95c74 Binary files /dev/null and b/titles/sao/data/1/PlayerRank.csv differ diff --git a/titles/sao/data/1/PlayerTraceTable.csv b/titles/sao/data/1/PlayerTraceTable.csv new file mode 100644 index 0000000..1d36d9a Binary files /dev/null and b/titles/sao/data/1/PlayerTraceTable.csv differ diff --git a/titles/sao/data/1/Property.csv b/titles/sao/data/1/Property.csv new file mode 100644 index 0000000..d7d8c30 Binary files /dev/null and b/titles/sao/data/1/Property.csv differ diff --git a/titles/sao/data/1/PropertyTable.csv b/titles/sao/data/1/PropertyTable.csv new file mode 100644 index 0000000..17a29bc Binary files /dev/null and b/titles/sao/data/1/PropertyTable.csv differ diff --git a/titles/sao/data/1/PropertyType.csv b/titles/sao/data/1/PropertyType.csv new file mode 100644 index 0000000..d028b0e Binary files /dev/null and b/titles/sao/data/1/PropertyType.csv differ diff --git a/titles/sao/data/1/QuestDropBoostCampaigns.csv b/titles/sao/data/1/QuestDropBoostCampaigns.csv new file mode 100644 index 0000000..347b692 Binary files /dev/null and b/titles/sao/data/1/QuestDropBoostCampaigns.csv differ diff --git a/titles/sao/data/1/QuestEpisodeAppendRewards.csv b/titles/sao/data/1/QuestEpisodeAppendRewards.csv new file mode 100644 index 0000000..26efbb1 Binary files /dev/null and b/titles/sao/data/1/QuestEpisodeAppendRewards.csv differ diff --git a/titles/sao/data/1/QuestExistUnit.csv b/titles/sao/data/1/QuestExistUnit.csv new file mode 100644 index 0000000..355b290 Binary files /dev/null and b/titles/sao/data/1/QuestExistUnit.csv differ diff --git a/titles/sao/data/1/QuestParam.csv b/titles/sao/data/1/QuestParam.csv new file mode 100644 index 0000000..705a3a3 Binary files /dev/null and b/titles/sao/data/1/QuestParam.csv differ diff --git a/titles/sao/data/1/QuestRareDrop.csv b/titles/sao/data/1/QuestRareDrop.csv new file mode 100644 index 0000000..0d9c8ce Binary files /dev/null and b/titles/sao/data/1/QuestRareDrop.csv differ diff --git a/titles/sao/data/QuestScene.csv b/titles/sao/data/1/QuestScene.csv similarity index 89% rename from titles/sao/data/QuestScene.csv rename to titles/sao/data/1/QuestScene.csv index 3e6b402..3307404 100644 Binary files a/titles/sao/data/QuestScene.csv and b/titles/sao/data/1/QuestScene.csv differ diff --git a/titles/sao/data/1/QuestSpecialRareDropSettings.csv b/titles/sao/data/1/QuestSpecialRareDropSettings.csv new file mode 100644 index 0000000..ed7ced5 Binary files /dev/null and b/titles/sao/data/1/QuestSpecialRareDropSettings.csv differ diff --git a/titles/sao/data/1/QuestSpecialRareDrops.csv b/titles/sao/data/1/QuestSpecialRareDrops.csv new file mode 100644 index 0000000..623a264 Binary files /dev/null and b/titles/sao/data/1/QuestSpecialRareDrops.csv differ diff --git a/titles/sao/data/1/QuestTutorial.csv b/titles/sao/data/1/QuestTutorial.csv new file mode 100644 index 0000000..ea45ad9 Binary files /dev/null and b/titles/sao/data/1/QuestTutorial.csv differ diff --git a/titles/sao/data/1/Rarity.csv b/titles/sao/data/1/Rarity.csv new file mode 100644 index 0000000..40f6f41 Binary files /dev/null and b/titles/sao/data/1/Rarity.csv differ diff --git a/titles/sao/data/1/ResEarnCampaignShopItems.csv b/titles/sao/data/1/ResEarnCampaignShopItems.csv new file mode 100644 index 0000000..e2bc6fd Binary files /dev/null and b/titles/sao/data/1/ResEarnCampaignShopItems.csv differ diff --git a/titles/sao/data/1/ResEarnCampaignShops.csv b/titles/sao/data/1/ResEarnCampaignShops.csv new file mode 100644 index 0000000..9413883 Binary files /dev/null and b/titles/sao/data/1/ResEarnCampaignShops.csv differ diff --git a/titles/sao/data/1/RewardSet.csv b/titles/sao/data/1/RewardSet.csv new file mode 100644 index 0000000..7858328 Binary files /dev/null and b/titles/sao/data/1/RewardSet.csv differ diff --git a/titles/sao/data/1/RewardTable.csv b/titles/sao/data/1/RewardTable.csv new file mode 100644 index 0000000..50ea22b Binary files /dev/null and b/titles/sao/data/1/RewardTable.csv differ diff --git a/titles/sao/data/1/SideQuest.csv b/titles/sao/data/1/SideQuest.csv new file mode 100644 index 0000000..970ce37 Binary files /dev/null and b/titles/sao/data/1/SideQuest.csv differ diff --git a/titles/sao/data/1/Skill.csv b/titles/sao/data/1/Skill.csv new file mode 100644 index 0000000..6fe8939 Binary files /dev/null and b/titles/sao/data/1/Skill.csv differ diff --git a/titles/sao/data/1/SkillCutInTextIndex.csv b/titles/sao/data/1/SkillCutInTextIndex.csv new file mode 100644 index 0000000..982ea23 Binary files /dev/null and b/titles/sao/data/1/SkillCutInTextIndex.csv differ diff --git a/titles/sao/data/1/SkillCutin.csv b/titles/sao/data/1/SkillCutin.csv new file mode 100644 index 0000000..ccae1b0 Binary files /dev/null and b/titles/sao/data/1/SkillCutin.csv differ diff --git a/titles/sao/data/1/SkillLevel.csv b/titles/sao/data/1/SkillLevel.csv new file mode 100644 index 0000000..2ed21d7 Binary files /dev/null and b/titles/sao/data/1/SkillLevel.csv differ diff --git a/titles/sao/data/1/SkillTable.csv b/titles/sao/data/1/SkillTable.csv new file mode 100644 index 0000000..4273c80 Binary files /dev/null and b/titles/sao/data/1/SkillTable.csv differ diff --git a/titles/sao/data/1/SoundBgm.csv b/titles/sao/data/1/SoundBgm.csv new file mode 100644 index 0000000..f43eedc Binary files /dev/null and b/titles/sao/data/1/SoundBgm.csv differ diff --git a/titles/sao/data/1/SoundSe.csv b/titles/sao/data/1/SoundSe.csv new file mode 100644 index 0000000..30a1699 Binary files /dev/null and b/titles/sao/data/1/SoundSe.csv differ diff --git a/titles/sao/data/1/SoundUi.csv b/titles/sao/data/1/SoundUi.csv new file mode 100644 index 0000000..0b2aa14 Binary files /dev/null and b/titles/sao/data/1/SoundUi.csv differ diff --git a/titles/sao/data/1/SoundVoice.csv b/titles/sao/data/1/SoundVoice.csv new file mode 100644 index 0000000..a4900b9 Binary files /dev/null and b/titles/sao/data/1/SoundVoice.csv differ diff --git a/titles/sao/data/1/Sound_SkillCutinVoice.csv b/titles/sao/data/1/Sound_SkillCutinVoice.csv new file mode 100644 index 0000000..a169923 Binary files /dev/null and b/titles/sao/data/1/Sound_SkillCutinVoice.csv differ diff --git a/titles/sao/data/1/SubStateMonster.csv b/titles/sao/data/1/SubStateMonster.csv new file mode 100644 index 0000000..b800149 Binary files /dev/null and b/titles/sao/data/1/SubStateMonster.csv differ diff --git a/titles/sao/data/SupportLog.csv b/titles/sao/data/1/SupportLog.csv similarity index 95% rename from titles/sao/data/SupportLog.csv rename to titles/sao/data/1/SupportLog.csv index 92295b1..1c64be5 100644 Binary files a/titles/sao/data/SupportLog.csv and b/titles/sao/data/1/SupportLog.csv differ diff --git a/titles/sao/data/1/SupportLogType.csv b/titles/sao/data/1/SupportLogType.csv new file mode 100644 index 0000000..fda48a1 Binary files /dev/null and b/titles/sao/data/1/SupportLogType.csv differ diff --git a/titles/sao/data/1/SynchroSkill.csv b/titles/sao/data/1/SynchroSkill.csv new file mode 100644 index 0000000..311b6dd Binary files /dev/null and b/titles/sao/data/1/SynchroSkill.csv differ diff --git a/titles/sao/data/1/Tips.csv b/titles/sao/data/1/Tips.csv new file mode 100644 index 0000000..8cf361c Binary files /dev/null and b/titles/sao/data/1/Tips.csv differ diff --git a/titles/sao/data/1/Title.csv b/titles/sao/data/1/Title.csv new file mode 100644 index 0000000..1218ddf Binary files /dev/null and b/titles/sao/data/1/Title.csv differ diff --git a/titles/sao/data/1/TreasureHuntTaskText.csv b/titles/sao/data/1/TreasureHuntTaskText.csv new file mode 100644 index 0000000..b737d5d Binary files /dev/null and b/titles/sao/data/1/TreasureHuntTaskText.csv differ diff --git a/titles/sao/data/1/TrialTower.csv b/titles/sao/data/1/TrialTower.csv new file mode 100644 index 0000000..ff51b87 Binary files /dev/null and b/titles/sao/data/1/TrialTower.csv differ diff --git a/titles/sao/data/1/UnanalyzedLogGrade.csv b/titles/sao/data/1/UnanalyzedLogGrade.csv new file mode 100644 index 0000000..b0bf28a Binary files /dev/null and b/titles/sao/data/1/UnanalyzedLogGrade.csv differ diff --git a/titles/sao/data/1/Unit.csv b/titles/sao/data/1/Unit.csv new file mode 100644 index 0000000..70fbca1 Binary files /dev/null and b/titles/sao/data/1/Unit.csv differ diff --git a/titles/sao/data/1/UnitCollision.csv b/titles/sao/data/1/UnitCollision.csv new file mode 100644 index 0000000..98eb0f9 Binary files /dev/null and b/titles/sao/data/1/UnitCollision.csv differ diff --git a/titles/sao/data/1/UnitGimmick.csv b/titles/sao/data/1/UnitGimmick.csv new file mode 100644 index 0000000..0c10a6c Binary files /dev/null and b/titles/sao/data/1/UnitGimmick.csv differ diff --git a/titles/sao/data/1/UnitPower.csv b/titles/sao/data/1/UnitPower.csv new file mode 100644 index 0000000..e9cf9b4 Binary files /dev/null and b/titles/sao/data/1/UnitPower.csv differ diff --git a/titles/sao/data/1/WeaponType.csv b/titles/sao/data/1/WeaponType.csv new file mode 100644 index 0000000..a6174b4 Binary files /dev/null and b/titles/sao/data/1/WeaponType.csv differ diff --git a/titles/sao/data/1/YuiMedalBonus.csv b/titles/sao/data/1/YuiMedalBonus.csv new file mode 100644 index 0000000..66bdb18 Binary files /dev/null and b/titles/sao/data/1/YuiMedalBonus.csv differ diff --git a/titles/sao/data/1/YuiMedalBonusCondition.csv b/titles/sao/data/1/YuiMedalBonusCondition.csv new file mode 100644 index 0000000..5f33487 Binary files /dev/null and b/titles/sao/data/1/YuiMedalBonusCondition.csv differ diff --git a/titles/sao/data/1/YuiMedalGetCondition.csv b/titles/sao/data/1/YuiMedalGetCondition.csv new file mode 100644 index 0000000..606ec4c Binary files /dev/null and b/titles/sao/data/1/YuiMedalGetCondition.csv differ diff --git a/titles/sao/data/1/YuiMedalShopItems.csv b/titles/sao/data/1/YuiMedalShopItems.csv new file mode 100644 index 0000000..6a12e42 Binary files /dev/null and b/titles/sao/data/1/YuiMedalShopItems.csv differ diff --git a/titles/sao/data/YuiMedalShops.csv b/titles/sao/data/1/YuiMedalShops.csv similarity index 60% rename from titles/sao/data/YuiMedalShops.csv rename to titles/sao/data/1/YuiMedalShops.csv index 5bac235..831d4f1 100644 Binary files a/titles/sao/data/YuiMedalShops.csv and b/titles/sao/data/1/YuiMedalShops.csv differ diff --git a/titles/sao/data/EquipmentLevel.csv b/titles/sao/data/EquipmentLevel.csv deleted file mode 100644 index 803bf7e..0000000 Binary files a/titles/sao/data/EquipmentLevel.csv and /dev/null differ diff --git a/titles/sao/data/GashaMedalShopItems.csv b/titles/sao/data/GashaMedalShopItems.csv deleted file mode 100644 index c5a46ae..0000000 Binary files a/titles/sao/data/GashaMedalShopItems.csv and /dev/null differ diff --git a/titles/sao/data/HeroLogLevel.csv b/titles/sao/data/HeroLogLevel.csv deleted file mode 100644 index 36a53b9..0000000 Binary files a/titles/sao/data/HeroLogLevel.csv and /dev/null differ diff --git a/titles/sao/data/PlayerRank.csv b/titles/sao/data/PlayerRank.csv deleted file mode 100644 index 9031661..0000000 Binary files a/titles/sao/data/PlayerRank.csv and /dev/null differ diff --git a/titles/sao/data/RareDropTable.csv b/titles/sao/data/RareDropTable.csv deleted file mode 100644 index cb674f6..0000000 Binary files a/titles/sao/data/RareDropTable.csv and /dev/null differ diff --git a/titles/sao/data/ResEarnCampaignShopItems.csv b/titles/sao/data/ResEarnCampaignShopItems.csv deleted file mode 100644 index 6d5dd17..0000000 Binary files a/titles/sao/data/ResEarnCampaignShopItems.csv and /dev/null differ diff --git a/titles/sao/data/ResEarnCampaignShops.csv b/titles/sao/data/ResEarnCampaignShops.csv deleted file mode 100644 index 2c201c9..0000000 Binary files a/titles/sao/data/ResEarnCampaignShops.csv and /dev/null differ diff --git a/titles/sao/data/RewardTable.csv b/titles/sao/data/RewardTable.csv deleted file mode 100644 index 929ff82..0000000 Binary files a/titles/sao/data/RewardTable.csv and /dev/null differ diff --git a/titles/sao/data/Title.csv b/titles/sao/data/Title.csv deleted file mode 100644 index 73b4dfd..0000000 Binary files a/titles/sao/data/Title.csv and /dev/null differ diff --git a/titles/sao/data/YuiMedalShopItems.csv b/titles/sao/data/YuiMedalShopItems.csv deleted file mode 100644 index f5d91fe..0000000 Binary files a/titles/sao/data/YuiMedalShopItems.csv and /dev/null differ diff --git a/titles/sao/frontend.py b/titles/sao/frontend.py new file mode 100644 index 0000000..401257f --- /dev/null +++ b/titles/sao/frontend.py @@ -0,0 +1,264 @@ +import yaml +import jinja2 +from typing import List +from starlette.requests import Request +from starlette.responses import Response, RedirectResponse, JSONResponse, PlainTextResponse +from starlette.routing import Route +from os import path +import random +from Crypto.Protocol.KDF import PBKDF2 +from Crypto.Hash import SHA1 +from Crypto.Cipher import AES, _mode_cbc + +from core.frontend import FE_Base, UserSession +from core.config import CoreConfig +from .database import SaoData +from .config import SaoConfig +from .const import SaoConstants + + +class SaoFrontend(FE_Base): + SN_PREFIX = SaoConstants.SERIAL_IDENT + NETID_PREFIX = SaoConstants.NETID_PREFIX + ALL_HEROS = [] + def __init__( + self, cfg: CoreConfig, environment: jinja2.Environment, cfg_dir: str + ) -> None: + super().__init__(cfg, environment) + self.data = SaoData(cfg) + self.game_cfg = SaoConfig() + if path.exists(f"{cfg_dir}/{SaoConstants.CONFIG_NAME}"): + self.game_cfg.update( + yaml.safe_load(open(f"{cfg_dir}/{SaoConstants.CONFIG_NAME}")) + ) + self.nav_name = "SAO" + self.card_key= None + self.card_iv = None + + if self.game_cfg.card.enable and self.game_cfg.card.crypt_password and self.game_cfg.card.crypt_salt: + hash = PBKDF2( + self.game_cfg.card.crypt_password, + bytes.fromhex(self.game_cfg.card.crypt_salt), + 48, + count=1000, + hmac_hash_module=SHA1, + ) + + self.card_key = hash[:32] + self.card_iv = hash[32:48] + + def get_routes(self) -> List[Route]: + return [ + Route("/", self.render_GET, methods=['GET']), + Route("/update.name", self.change_name, methods=['POST']), + Route("/matching.auth", self.matching_auth, methods=['POST']), + Route("/matching.auth/", self.matching_auth, methods=['POST']), + Route("/qr.read", self.read_qr, methods=['POST']), + Route("/qr.register", self.reg_qr, methods=['POST']), + Route("/profile.register", self.reg_profile, methods=['POST']) + ] + + async def render_GET(self, request: Request) -> Response: + template = self.environment.get_template( + "titles/sao/templates/sao_index.jinja" + ) + pf = None + + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() + + else: + profile = await self.data.profile.get_profile(usr_sesh.user_id) + if profile is not None: + pf = profile._asdict() + if not self.ALL_HEROS: + self.ALL_HEROS = await self.data.static.get_heros() + + err = 0 + suc = 0 + if "e" in request.query_params: + err = request.query_params.get("e", 0) + if "s" in request.query_params: + suc = request.query_params.get("s", 0) + + return Response(template.render( + title=f"{self.core_config.server.name} | {self.nav_name}", + game_list=self.environment.globals["game_list"], + sesh=vars(usr_sesh), + profile=pf, + success=int(suc), + error=int(err), + all_heros=self.ALL_HEROS if self.ALL_HEROS else [] + ), media_type="text/html; charset=utf-8") + + async def change_name(self, request: Request) -> RedirectResponse: + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/game/sao/", 303) + + frm = await request.form() + new_name = frm.get("new_name") + + if len(new_name) > 16: + return RedirectResponse("/game/sao/?e=8", 303) + + await self.data.profile.set_profile_name(usr_sesh.user_id, new_name) + self.logger.info(f"User {usr_sesh.user_id} changed name to {new_name}") + + return RedirectResponse("/game/sao/?s=1", 303) + + async def matching_auth(self, request: Request) -> JSONResponse: + self.logger.debug(f"Mathing auth params: {request.query_params}") + self.logger.debug(f"Mathing auth headers: {request.headers}") + uid = request.query_params.get('userId', '') + + if not uid: + uid = f'Guest{str(random.randint(1,9999)).zfill(4)}' + self.logger.info(f"Matching auth request with no userId, using {uid}") + + else: + self.logger.info(f"Matching auth request for userId {uid}") + + return JSONResponse({ "ResultCode": 1, "UserId": uid }) # Just auth everything for now + + async def read_qr(self, request: Request) -> PlainTextResponse: + if not self.card_key or not self.card_iv: + return PlainTextResponse("e13-1", 400) + + usr_sesh = self.validate_session(request) + if not usr_sesh: + return PlainTextResponse("e9", 403) + + frm = await request.form() + qr_data = frm.get("qr_data", "") + if not qr_data.isalnum() or not len(qr_data) == 0x40: + return PlainTextResponse("e14-1", 400) + + try: + cipher: _mode_cbc.CbcMode = AES.new(self.card_key, AES.MODE_CBC, iv=self.card_iv) + except Exception as e: + self.logger.error(f"Error creating card cipher - {e}") + return PlainTextResponse("e13-2", 500) + sn = b"" + try: + sn = cipher.decrypt(bytes.fromhex(qr_data))[:19] + sn = sn.decode() + except Exception as e: + self.logger.error(f"Error decrypting card data {qr_data} ({sn}) - {e}") + return PlainTextResponse("e14-2", 400) + + if not sn.isnumeric(): + self.logger.error(f"Card serial {sn} decrypted incorrectly") + return PlainTextResponse("e13-3", 400) + + return PlainTextResponse(sn) + + async def reg_qr(self, request: Request) -> RedirectResponse: + if not self.card_key or not self.card_iv: + return RedirectResponse("/game/sao/?e=14", 303) + + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/game/sao/?e=9", 303) + + frm = await request.form() + serial = frm.get("qr_register_serial") + hero = frm.get("qr_register_hero") + is_holo = bool(frm.get("qr_register_holo", False)) + + user_hero = await self.data.item.get_hero_log(usr_sesh.user_id, hero) + if not user_hero: + hero_statc = await self.data.static.get_hero_by_id(hero) + if not hero_statc: + self.logger.error(f"Failed to find hero log {hero}! Please run the reader") + return RedirectResponse(" /game/sao/?e=13", 303) + + skills = await self.data.static.get_skill_table_by_subid(hero_statc['SkillTableSubId']) + if not skills: + self.logger.error(f"Failed to find skill table {hero_statc['SkillTableSubId']}! Please run the reader") + return RedirectResponse("/game/sao/?e=13", 303) + + default_skills = [] + now_have_skills = [None, None, None, None, None] + x = 0 + for skill in skills: + if skill['LevelObtained'] == 1 and skill['AwakeningId'] == 0: + default_skills[x] = skill['SkillId'] + x += 1 + if x >= 5: + break + + for skill in default_skills: + skill_info = await self.data.static.get_skill_by_id(skill) + skill_slot = skill_info['Level'] - 1 + if now_have_skills[skill_slot] is not None: + now_have_skills[skill] + + + user_hero_id = await self.data.item.put_hero_log( + usr_sesh.user_id, + hero, + 1, + 0, + hero_statc['DefaultEquipmentId1'], + hero_statc['DefaultEquipmentId2'], + now_have_skills[0], + now_have_skills[1], + now_have_skills[2], + now_have_skills[3], + now_have_skills[4] + ) + if not user_hero_id: + self.logger.error(f"Failed to give user {usr_sesh.user_id} hero {hero}!") + return RedirectResponse("/game/sao/?e=99", 303) + else: + user_hero_id = user_hero['id'] + + card_id = await self.data.profile.put_hero_card(usr_sesh.user_id, serial, user_hero_id, is_holo) + if not card_id: + self.logger.error(f"Failed to give user {usr_sesh.user_id} hero card {hero}!") + return RedirectResponse("/game/sao/?e=99", 303) + + self.logger.info(f"User {usr_sesh.user_id} added hero {hero} as card with id {card_id}") + + return RedirectResponse("/game/sao/?s=1", 303) + + async def reg_profile(self, request: Request) -> RedirectResponse: + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/game/sao/?e=9", 303) + + frm = await request.form() + name = frm.get("sao_register_username") + + if len(name) > 16: + return RedirectResponse("/game/sao/?e=8", 303) + + profile_id = await self.data.profile.create_profile(usr_sesh.user_id) + if not profile_id: + self.logger.error(f"Failed to web register User {usr_sesh.user_id} with name {name}") + return RedirectResponse("/game/sao/?e=99", 303) + + await self.data.profile.set_profile_name(usr_sesh.user_id, name) + + equip1 = await self.data.item.put_equipment(usr_sesh.user_id, 101000000) + equip2 = await self.data.item.put_equipment(usr_sesh.user_id, 102000000) + equip3 = await self.data.item.put_equipment(usr_sesh.user_id, 109000000) + if not equip1 or not equip2 or not equip3: + self.logger.error(f"Failed to create profile for user {usr_sesh.user_id} from (could not add equipment)") + return RedirectResponse("/game/sao/?e=98", 303) + + hero1 = await self.data.item.put_hero_log(usr_sesh.user_id, 101000010, 1, 0, equip1, None, 1002, 1003, 1014, None, None) + hero2 = await self.data.item.put_hero_log(usr_sesh.user_id, 102000010, 1, 0, equip2, None, 3001, 3002, 3004, None, None) + hero3 = await self.data.item.put_hero_log(usr_sesh.user_id, 105000010, 1, 0, equip3, None, 10005, 10002, 10004, None, None) + if not hero1 or not hero2 or not hero3: + self.logger.error(f"Failed to create profile for user {usr_sesh.user_id} (could not add heros)") + return RedirectResponse("/game/sao/?e=97", 303) + + await self.data.item.put_hero_party(usr_sesh.user_id, 0, hero1, hero2, hero3) + # await self.data.item.put_player_quest(usr_sesh.user_id, 1001, True, 300, 0, 0, 1) + # Force the tutorial stage to be completed due to potential crash in-game + self.logger.info(f"Web registered User {usr_sesh.user_id} profile {profile_id} with name {name}") + + return RedirectResponse("/game/sao/?s=1", 303) \ No newline at end of file diff --git a/titles/sao/handlers/base.py b/titles/sao/handlers/base.py index 2f2f13e..e2cb9d1 100644 --- a/titles/sao/handlers/base.py +++ b/titles/sao/handlers/base.py @@ -1,10 +1,9 @@ import struct from datetime import datetime -from typing import List -from construct import * +from typing import List, Union from .helpers import * -import csv -from csv import * + +from ..const import GameconnectCmd class SaoRequestHeader: def __init__(self, data: bytes) -> None: @@ -21,10 +20,9 @@ class SaoRequestHeader: class SaoBaseRequest: def __init__(self, header: SaoRequestHeader, data: bytes) -> None: self.header = header - # TODO: Length check class SaoResponseHeader: - def __init__(self, cmd_id: int) -> None: + def __init__(self, cmd_id: GameconnectCmd) -> None: self.cmd = cmd_id self.err_status = 0 self.error_type = 0 @@ -37,12 +35,40 @@ class SaoResponseHeader: return struct.pack("!HHIIIII", self.cmd, self.err_status, self.error_type, self.vendor_id, self.game_id, self.version_id, self.length) class SaoBaseResponse: - def __init__(self, cmd_id: int) -> None: - self.header = SaoResponseHeader(cmd_id) + def __init__(self, cmd_id: Union[GameconnectCmd, int]) -> None: + if type(cmd_id) == int: + cmd = GameconnectCmd(cmd_id) + else: + cmd = cmd_id + self.header = SaoResponseHeader(cmd) def make(self) -> bytes: return self.header.make() +class SaoGenericUserTicketRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + ticket_id = decode_str(data, off) + self.ticket_id = ticket_id[0] + off += ticket_id[1] + + user_id = decode_str(data, off) + self.user_id = user_id[0] + off += user_id[1] + +class SaoGenericUserRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + +class SaoNoopRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + self.dummy = decode_byte(data, 0) + class SaoNoopResponse(SaoBaseResponse): def __init__(self, cmd: int) -> None: super().__init__(cmd) @@ -51,154 +77,164 @@ class SaoNoopResponse(SaoBaseResponse): def make(self) -> bytes: return super().make() + struct.pack("!bI", self.result, 0) - -class SaoGetMaintRequest(SaoBaseRequest): + +class SaoTicketRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) - # TODO: The rest of the mait info request + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off -class SaoGetMaintResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) - self.result = 1 - self.maint_begin = datetime.fromtimestamp(0) - self.maint_begin_int_ct = 6 - self.maint_end = datetime.fromtimestamp(0) - self.maint_end_int_ct = 6 - self.dt_format = "%Y%m%d%H%M%S" +class SaoTicketResponse(SaoBaseResponse): + def __init__(self, ticket_id: str = "9") -> None: + super().__init__(GameconnectCmd.TICKET_RESPONSE) + self.result = "1" + self.ticket_id = ticket_id def make(self) -> bytes: - maint_begin_list = [x for x in datetime.strftime(self.maint_begin, self.dt_format)] - maint_end_list = [x for x in datetime.strftime(self.maint_end, self.dt_format)] - self.maint_begin_int_ct = len(maint_begin_list) * 2 - self.maint_end_int_ct = len(maint_end_list) * 2 + return super().make() \ + + encode_str(self.result) \ + + encode_str(self.ticket_id) - maint_begin_bytes = b"" - maint_end_bytes = b"" - - for x in maint_begin_list: - maint_begin_bytes += struct.pack(" None: + super().__init__(GameconnectCmd.GET_MAINTENANCE_INFO_RESPONSE) + self.result = 1 + self.maint_begin = maint_start + self.maint_end = maint_end + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_date_str(self.maint_begin) \ + + encode_date_str(self.maint_end) class SaoCommonAcCabinetBootNotificationRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF -class SaoCommonAcCabinetBootNotificationResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) - self.result = 1 - - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - ) + self.net_id, new_off = decode_str(data, off) + off += new_off - resp_data = resp_struct.build(dict( - result=self.result, - )) + self.place_id, new_off = decode_str(data, off) + off += new_off - self.length = len(resp_data) - return super().make() + resp_data + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.store_name, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.current_version_app_id = decode_int(data, off) + off += INT_OFF + + self.current_master_data_version = decode_int(data, off) + off += INT_OFF class SaoMasterDataVersionCheckRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + self.current_data_version = decode_int(data, 0) class SaoMasterDataVersionCheckResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self, current_ver: int, game_ver: int) -> None: + super().__init__(GameconnectCmd.MASTER_DATA_VERSION_CHECK_RESPONSE) self.result = 1 - self.update_flag = 0 - self.data_version = 100 + self.update_flag = 1 if game_ver != current_ver else 0 + self.data_version = current_ver def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "update_flag" / Int8ul, # result is either 0 or 1 - "data_version" / Int32ub, - ) - - resp_data = resp_struct.build(dict( - result=self.result, - update_flag=self.update_flag, - data_version=self.data_version, - )) - - self.length = len(resp_data) + resp_data = encode_byte(self.result) + resp_data += encode_byte(self.update_flag) + resp_data += encode_int(self.data_version) return super().make() + resp_data -class SaoCommonGetAppVersionsRequest(SaoBaseRequest): +class SaoGetAppVersionsResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_APP_VERSIONS_RESPONSE) + self.result = 1 + self.data_list: List[AppVersionData] = [] + + def make(self) -> bytes: + ret = encode_byte(self.result) + ret += encode_arr_cls(self.data_list) + return super().make() + ret + +class SaoPayingPlayStartRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.paying_user_id, new_off = decode_str(data, off) + off += new_off -class SaoCommonGetAppVersionsRequest(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + self.game_sub_id, new_off = decode_str(data, off) + off += new_off + + self.net_id, new_off = decode_str(data, off) + off += new_off + + self.place_id, new_off = decode_str(data, off) + off += new_off + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.store_name, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + +class SaoPayingPlayStartResponse(SaoBaseResponse): + def __init__(self, session_id: str = "1") -> None: + super().__init__(GameconnectCmd.PAYING_PLAY_START_RESPONSE) self.result = 1 - self.data_list_size = 1 # Number of arrays - - self.version_app_id = 1 - self.applying_start_date = "20230520193000" + self.paying_session_id = session_id def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "data_list_size" / Int32ub, + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.paying_session_id) - "version_app_id" / Int32ub, - "applying_start_date_size" / Int32ub, # big endian - "applying_start_date" / Int16ul[len(self.applying_start_date)], - ) - - resp_data = resp_struct.build(dict( - result=self.result, - data_list_size=self.data_list_size, - - version_app_id=self.version_app_id, - applying_start_date_size=len(self.applying_start_date) * 2, - applying_start_date=[ord(x) for x in self.applying_start_date], - )) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoCommonPayingPlayStartRequest(SaoBaseRequest): +class SaoPayingPlayEndRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.paying_session_id, new_off = decode_str(data, off) + off += new_off -class SaoCommonPayingPlayStartRequest(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) - self.result = 1 - self.paying_session_id = "1" - - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "paying_session_id_size" / Int32ub, # big endian - "paying_session_id" / Int16ul[len(self.paying_session_id)], - ) + self.paying_user_id, new_off = decode_str(data, off) + off += new_off - resp_data = resp_struct.build(dict( - result=self.result, - paying_session_id_size=len(self.paying_session_id) * 2, - paying_session_id=[ord(x) for x in self.paying_session_id], - )) + self.net_id, new_off = decode_str(data, off) + off += new_off - self.length = len(resp_data) - return super().make() + resp_data + self.place_id, new_off = decode_str(data, off) + off += new_off + + self.played_date, new_off = decode_str(data, off) + off += new_off + + self.played_type = decode_short(data, off) + off += SHORT_OFF + + self.played_amount = decode_short(data, off) + off += SHORT_OFF + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.store_name, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off class SaoGetAuthCardDataRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: @@ -207,7 +243,7 @@ class SaoGetAuthCardDataRequest(SaoBaseRequest): self.cabinet_type = decode_byte(data, off) off += BYTE_OFF - self.auth_type = decode_byte(data, off) + self.auth_type = AuthType(decode_byte(data, off)) # 0 is unknown, 1 is card (banapass, aime, AICC), 2 is moble off += BYTE_OFF store_id = decode_str(data, off) @@ -226,147 +262,90 @@ class SaoGetAuthCardDataRequest(SaoBaseRequest): self.chip_id = chip_id[0] off += chip_id[1] -class SaoGetAuthCardDataResponse(SaoBaseResponse): #GssSite.dll / GssSiteSystem / GameConnectProt / public class get_auth_card_data_R : GameConnect.GssProtocolBase - def __init__(self, cmd, profile_data) -> None: - super().__init__(cmd) - +class SaoGetAuthCardDataResponse(SaoBaseResponse): + def __init__(self, nicknname: str, user_id: int) -> None: + super().__init__(GameconnectCmd.GET_AUTH_CARD_DATA_RESPONSE) self.result = 1 self.unused_card_flag = "" self.first_play_flag = 0 self.tutorial_complete_flag = 1 - self.nick_name = profile_data['nick_name'] # nick_name field #4 - self.personal_id = str(profile_data['user']) + self.nick_name = nicknname + self.personal_id = str(user_id) def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "unused_card_flag_size" / Int32ub, # big endian - "unused_card_flag" / Int16ul[len(self.unused_card_flag)], - "first_play_flag" / Int8ul, # result is either 0 or 1 - "tutorial_complete_flag" / Int8ul, # result is either 0 or 1 - "nick_name_size" / Int32ub, # big endian - "nick_name" / Int16ul[len(self.nick_name)], - "personal_id_size" / Int32ub, # big endian - "personal_id" / Int16ul[len(self.personal_id)] - ) + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.unused_card_flag) \ + + encode_byte(self.first_play_flag) \ + + encode_byte(self.tutorial_complete_flag) \ + + encode_str(self.nick_name) \ + + encode_str(self.personal_id) - resp_data = resp_struct.build(dict( - result=self.result, - unused_card_flag_size=len(self.unused_card_flag) * 2, - unused_card_flag=[ord(x) for x in self.unused_card_flag], - first_play_flag=self.first_play_flag, - tutorial_complete_flag=self.tutorial_complete_flag, - nick_name_size=len(self.nick_name) * 2, - nick_name=[ord(x) for x in self.nick_name], - personal_id_size=len(self.personal_id) * 2, - personal_id=[ord(x) for x in self.personal_id] - )) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoHomeCheckAcLoginBonusRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - -class SaoHomeCheckAcLoginBonusResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) - self.result = 1 - self.reward_get_flag = 1 - self.get_ac_login_bonus_id_list_size = 2 # Array - - self.get_ac_login_bonus_id_1 = 1 # "2020年7月9日~(アニメ&リコリス記念)" - self.get_ac_login_bonus_id_2 = 2 # "2020年10月6日~(秋のデビュー&カムバックCP)" - - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "reward_get_flag" / Int8ul, # result is either 0 or 1 - "get_ac_login_bonus_id_list_size" / Int32ub, - - "get_ac_login_bonus_id_1" / Int32ub, - "get_ac_login_bonus_id_2" / Int32ub, - ) - - resp_data = resp_struct.build(dict( - result=self.result, - reward_get_flag=self.reward_get_flag, - get_ac_login_bonus_id_list_size=self.get_ac_login_bonus_id_list_size, - - get_ac_login_bonus_id_1=self.get_ac_login_bonus_id_1, - get_ac_login_bonus_id_2=self.get_ac_login_bonus_id_2, - )) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetQuestSceneMultiPlayPhotonServerRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - -class SaoGetQuestSceneMultiPlayPhotonServerResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) - self.result = 1 - self.application_id = "7df3a2f6-d69d-4073-aafe-810ee61e1cea" - - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "application_id_size" / Int32ub, # big endian - "application_id" / Int16ul[len(self.application_id)], - ) - - resp_data = resp_struct.build(dict( - result=self.result, - application_id_size=len(self.application_id) * 2, - application_id=[ord(x) for x in self.application_id], - )) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoTicketRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - -class SaoTicketResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) - self.result = "1" - self.ticket_id = "9" #up to 18 - - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result_size" / Int32ub, # big endian - "result" / Int16ul[len(self.result)], - "ticket_id_size" / Int32ub, # big endian - "ticket_id" / Int16ul[len(self.result)], - ) - - resp_data = resp_struct.build(dict( - result_size=len(self.result) * 2, - result=[ord(x) for x in self.result], - ticket_id_size=len(self.ticket_id) * 2, - ticket_id=[ord(x) for x in self.ticket_id], - )) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoCommonLoginRequest(SaoBaseRequest): +class SaoGetAccessCodeByKeitaiRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) off = 0 self.cabinet_type = decode_byte(data, off) off += BYTE_OFF - self.auth_type = decode_byte(data, off) + self.auth_type = AuthType(decode_byte(data, off)) # 0 is unknown, 1 is card (banapass, aime, AICC), 2 is moble + off += BYTE_OFF + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.chip_id, new_off = decode_str(data, off) + off += new_off + +class SaoGetAccessCodeByKeitaiResponse(SaoBaseResponse): + def __init__(self, access_code: str) -> None: + super().__init__(GameconnectCmd.GET_ACCESS_CODE_BY_KEITAI_RESPONSE) + self.result = 1 + self.access_code = access_code + self.ba_id_flag = 1 + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.access_code) \ + + encode_byte(self.ba_id_flag) + +class SaoCheckAcLoginBonusResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.CHECK_AC_LOGIN_BONUS_RESPONSE) + self.result = 1 + self.reward_get_flag = 1 + + self.get_ac_login_bonus_id_list: List[int] = [] # "2020年7月9日~(アニメ&リコリス記念)" + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_byte(self.reward_get_flag) \ + + encode_arr_num(self.get_ac_login_bonus_id_list, INT_OFF) \ + +class SaoGetQuestSceneMultiPlayPhotonServerResponse(SaoBaseResponse): + def __init__(self, app_id: str = "7df3a2f6-d69d-4073-aafe-810ee61e1cea") -> None: + super().__init__(GameconnectCmd.GET_QUEST_SCENE_MULTI_PLAY_PHOTON_SERVER_RESPONSE) + self.result = 1 + self.application_id = app_id + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.application_id) + +class SaoLoginRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + + self.auth_type = AuthType(decode_byte(data, off)) # 0 is unknown, 1 is card (banapass, aime, AICC), 2 is moble off += BYTE_OFF store_id = decode_str(data, off) @@ -392,1107 +371,555 @@ class SaoCommonLoginRequest(SaoBaseRequest): self.free_ticket_distribution_target_flag = decode_byte(data, off) off += BYTE_OFF -class SaoCommonLoginResponse(SaoBaseResponse): - def __init__(self, cmd, profile_data) -> None: - super().__init__(cmd) +class SaoLoginResponse(SaoBaseResponse): + def __init__(self, user_id: int, first_play: bool = False, logged_in_today: bool = True) -> None: + super().__init__(GameconnectCmd.LOGIN_RESPONSE) self.result = 1 - self.user_id = str(profile_data['user']) - self.first_play_flag = 0 - self.grantable_free_ticket_flag = 1 - self.login_reward_vp = 99 - self.today_paying_flag = 1 + self.user_id = str(user_id) + self.first_play_flag = first_play + self.grantable_free_ticket_flag = not logged_in_today + self.login_reward_vp = 0 if logged_in_today else 100 + self.today_paying_flag = logged_in_today def make(self) -> bytes: - # create a resp struct - ''' - bool = Int8ul - short = Int16ub - int = Int32ub - ''' - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "user_id_size" / Int32ub, # big endian - "user_id" / Int16ul[len(self.user_id)], - "first_play_flag" / Int8ul, # result is either 0 or 1 - "grantable_free_ticket_flag" / Int8ul, # result is either 0 or 1 - "login_reward_vp" / Int16ub, - "today_paying_flag" / Int8ul, # result is either 0 or 1 - ) + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.user_id) \ + + encode_byte(self.first_play_flag) \ + + encode_byte(self.grantable_free_ticket_flag) \ + + encode_short(self.login_reward_vp) \ + + encode_byte(self.today_paying_flag) - resp_data = resp_struct.build(dict( - result=self.result, - user_id_size=len(self.user_id) * 2, - user_id=[ord(x) for x in self.user_id], - first_play_flag=self.first_play_flag, - grantable_free_ticket_flag=self.grantable_free_ticket_flag, - login_reward_vp=self.login_reward_vp, - today_paying_flag=self.today_paying_flag, - )) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoCheckComebackEventRequest(SaoBaseRequest): +class SaoLogoutRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off -class SaoCheckComebackEventRequest(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.cabinet_type = decode_byte(data, off) # 0 satalite, 1 terminal + off += BYTE_OFF + + self.remaining_ticket_num = decode_short(data, off) + off += SHORT_OFF + +class SaoPurchaseTicketRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.discount_type = decode_byte(data, off) + off += BYTE_OFF + self.purchase_num = decode_byte(data, off) + off += BYTE_OFF + +class SaoConsumeTicketRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.discount_type = decode_byte(data, off) + off += BYTE_OFF + self.act_type = ActTypeConsumeTicket(decode_byte(data, off)) + off += BYTE_OFF + self.consume_num = decode_byte(data, off) + off += BYTE_OFF + +class SaoAddCreditRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + self.add_num = decode_byte(data, off) + off += BYTE_OFF + +class SaoConsumeCreditRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + self.act_type = ActTypeConsumeCredit(decode_byte(data, off)) + off += BYTE_OFF + self.consume_num = decode_byte(data, off) + off += BYTE_OFF + +class SaoPurchaseTicketGuestRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + self.discount_type = decode_byte(data, off) + off += BYTE_OFF + self.purchase_num = decode_byte(data, off) + off += BYTE_OFF + +class SaoConsumeTicketGuestRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + self.discount_type = decode_byte(data, off) + off += BYTE_OFF + self.act_type = ActTypeConsumeTicket(decode_byte(data, off)) + off += BYTE_OFF + self.consume_num = decode_byte(data, off) + off += BYTE_OFF + +class SaoAddCreditGuestRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + self.add_num = decode_byte(data, off) + off += BYTE_OFF + +class SaoConsumeCreditGuestRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + self.act_type = ActTypeConsumeCredit(decode_byte(data, off)) + off += BYTE_OFF + self.consume_num = decode_byte(data, off) + off += BYTE_OFF + +class SaoCheckComebackEventResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.CHECK_COMEBACK_EVENT_RESPONSE) self.result = 1 - self.get_flag_ = 1 - self.get_comeback_event_id_list = "" # Array of events apparently + self.get_flag = 1 + self.get_comeback_event_id_list: List[int] = [] # Array of events apparently def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "get_flag_" / Int8ul, # result is either 0 or 1 - "get_comeback_event_id_list_size" / Int32ub, # big endian - "get_comeback_event_id_list" / Int16ul[len(self.get_comeback_event_id_list)], - ) + return super().make() \ + + encode_byte(self.result) \ + + encode_byte(self.get_flag) \ + + encode_arr_num(self.get_comeback_event_id_list, INT_OFF) - resp_data = resp_struct.build(dict( - result=self.result, - get_flag_=self.get_flag_, - get_comeback_event_id_list_size=len(self.get_comeback_event_id_list) * 2, - get_comeback_event_id_list=[ord(x) for x in self.get_comeback_event_id_list], - )) +class SaoChangeMyStoreRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off - self.length = len(resp_data) - return super().make() + resp_data + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.store_id, new_off = decode_str(data, off) + off += new_off + +class SaoCheckTitleGetDecisionRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_offset = decode_str(data, off) + off += new_offset + + self.user_id, new_offset = decode_str(data, off) + off += new_offset + +class SaoCheckTitleGetDecisionResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.CHECK_TITLE_GET_DECISION_RESPONSE) + self.result = 1 + self.get_title_id_list: List[int] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_num(self.get_title_id_list, INT_OFF) class SaoGetUserBasicDataRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) - self.user_id = decode_str(data, 0)[0] + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off class SaoGetUserBasicDataResponse(SaoBaseResponse): - def __init__(self, cmd, profile_data) -> None: - super().__init__(cmd) + def __init__(self, profile_data) -> None: + super().__init__(GameconnectCmd.GET_USER_BASIC_DATA_RESPONSE) self.result = 1 - self.user_basic_data_size = 1 # Number of arrays - self.user_type = profile_data['user_type'] - self.nick_name = profile_data['nick_name'] - self.rank_num = profile_data['rank_num'] - self.rank_exp = profile_data['rank_exp'] - self.own_col = profile_data['own_col'] - self.own_vp = profile_data['own_vp'] - self.own_yui_medal = profile_data['own_yui_medal'] - self.setting_title_id = profile_data['setting_title_id'] - self.favorite_user_hero_log_id = "" - self.favorite_user_support_log_id = "" - self.my_store_id = "1" - self.my_store_name = "ARTEMiS" - self.user_reg_date = "20230101120000" - + self.user_basic_data: List[UserBasicData] = [UserBasicData.from_args(profile_data)] + def make(self) -> bytes: - # create a resp struct - ''' - bool = Int8ul - short = Int16ub - int = Int32ub - ''' - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "user_basic_data_size" / Int32ub, + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.user_basic_data) - "user_type" / Int16ub, - "nick_name_size" / Int32ub, # big endian - "nick_name" / Int16ul[len(self.nick_name)], - "rank_num" / Int16ub, - "rank_exp" / Int32ub, - "own_col" / Int32ub, - "own_vp" / Int32ub, - "own_yui_medal" / Int32ub, - "setting_title_id" / Int32ub, - "favorite_user_hero_log_id_size" / Int32ub, # big endian - "favorite_user_hero_log_id" / Int16ul[len(str(self.favorite_user_hero_log_id))], - "favorite_user_support_log_id_size" / Int32ub, # big endian - "favorite_user_support_log_id" / Int16ul[len(str(self.favorite_user_support_log_id))], - "my_store_id_size" / Int32ub, # big endian - "my_store_id" / Int16ul[len(str(self.my_store_id))], - "my_store_name_size" / Int32ub, # big endian - "my_store_name" / Int16ul[len(str(self.my_store_name))], - "user_reg_date_size" / Int32ub, # big endian - "user_reg_date" / Int16ul[len(self.user_reg_date)] - - ) - - resp_data = resp_struct.build(dict( - result=self.result, - user_basic_data_size=self.user_basic_data_size, - - user_type=self.user_type, - nick_name_size=len(self.nick_name) * 2, - nick_name=[ord(x) for x in self.nick_name], - rank_num=self.rank_num, - rank_exp=self.rank_exp, - own_col=self.own_col, - own_vp=self.own_vp, - own_yui_medal=self.own_yui_medal, - setting_title_id=self.setting_title_id, - favorite_user_hero_log_id_size=len(self.favorite_user_hero_log_id) * 2, - favorite_user_hero_log_id=[ord(x) for x in str(self.favorite_user_hero_log_id)], - favorite_user_support_log_id_size=len(self.favorite_user_support_log_id) * 2, - favorite_user_support_log_id=[ord(x) for x in str(self.favorite_user_support_log_id)], - my_store_id_size=len(self.my_store_id) * 2, - my_store_id=[ord(x) for x in str(self.my_store_id)], - my_store_name_size=len(self.my_store_name) * 2, - my_store_name=[ord(x) for x in str(self.my_store_name)], - user_reg_date_size=len(self.user_reg_date) * 2, - user_reg_date=[ord(x) for x in self.user_reg_date], - )) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetHeroLogUserDataListRequest(SaoBaseRequest): +class SaoGetVpGashaTicketDataListRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) - self.user_id = decode_str(data, 0)[0] + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + +class SaoGetVpGashaTicketDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_VP_GASHA_TICKET_DATA_LIST_RESPONSE) + self.result = 1 + self.vp_gasha_ticket_data_list: List[VpGashaTicketData] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.vp_gasha_ticket_data_list) + +class SaoChangeTitleRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.user_title_id, new_off = decode_str(data, off) + off += new_off + +class SaoGetPresentBoxNumResponse(SaoBaseResponse): + def __init__(self, num_box: int = 0, max_num: int = 0) -> None: + super().__init__(GameconnectCmd.GET_PRESENT_BOX_NUM_RESPONSE) + self.result = 1 + self.num = num_box + self.max_num = max_num + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_short(self.num) \ + + encode_short(self.max_num) class SaoGetHeroLogUserDataListResponse(SaoBaseResponse): - def __init__(self, cmd, hero_data) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_HERO_LOG_USER_DATA_LIST_RESPONSE) self.result = 1 - self.user_hero_log_id = [] - self.log_level = [] - self.max_log_level_extended_num = [] - self.log_exp = [] - self.last_set_skill_slot1_skill_id = [] - self.last_set_skill_slot2_skill_id = [] - self.last_set_skill_slot3_skill_id = [] - self.last_set_skill_slot4_skill_id = [] - self.last_set_skill_slot5_skill_id = [] - - for i in range(len(hero_data)): - - # Calculate level based off experience and the CSV list - with open(r'titles/sao/data/HeroLogLevel.csv') as csv_file: - csv_reader = csv.reader(csv_file, delimiter=',') - line_count = 0 - data = [] - rowf = False - for row in csv_reader: - if rowf==False: - rowf=True - else: - data.append(row) - - exp = hero_data[i][4] - - for e in range(0,len(data)): - if exp>=int(data[e][1]) and exp bytes: - #new stuff - - hero_log_user_data_list_struct = Struct( - "user_hero_log_id_size" / Int32ub, # big endian - "user_hero_log_id" / Int16ul[9], #string - "hero_log_id" / Int32ub, #int - "log_level" / Int16ub, #short - "max_log_level_extended_num" / Int16ub, #short - "log_exp" / Int32ub, #int - "possible_awakening_flag" / Int8ul, # result is either 0 or 1 - "awakening_stage" / Int16ub, #short - "awakening_exp" / Int32ub, #int - "skill_slot_correction_value" / Int8ul, # result is either 0 or 1 - "last_set_skill_slot1_skill_id" / Int16ub, #short - "last_set_skill_slot2_skill_id" / Int16ub, #short - "last_set_skill_slot3_skill_id" / Int16ub, #short - "last_set_skill_slot4_skill_id" / Int16ub, #short - "last_set_skill_slot5_skill_id" / Int16ub, #short - "property1_property_id" / Int32ub, - "property1_value1" / Int32ub, - "property1_value2" / Int32ub, - "property2_property_id" / Int32ub, - "property2_value1" / Int32ub, - "property2_value2" / Int32ub, - "property3_property_id" / Int32ub, - "property3_value1" / Int32ub, - "property3_value2" / Int32ub, - "property4_property_id" / Int32ub, - "property4_value1" / Int32ub, - "property4_value2" / Int32ub, - "converted_card_num" / Int16ub, - "shop_purchase_flag" / Int8ul, # result is either 0 or 1 - "protect_flag" / Int8ul, # result is either 0 or 1 - "get_date_size" / Int32ub, # big endian - "get_date" / Int16ul[len(self.get_date)], - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "hero_log_user_data_list_size" / Rebuild(Int32ub, len_(this.hero_log_user_data_list)), # big endian - "hero_log_user_data_list" / Array(this.hero_log_user_data_list_size, hero_log_user_data_list_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - hero_log_user_data_list_size=0, - hero_log_user_data_list=[], - ))) - - for i in range(len(self.hero_log_id)): - hero_data = dict( - user_hero_log_id_size=len(self.user_hero_log_id[i]) * 2, - user_hero_log_id=[ord(x) for x in self.user_hero_log_id[i]], - hero_log_id=self.hero_log_id[i], - log_level=self.log_level[i], - max_log_level_extended_num=self.max_log_level_extended_num[i], - log_exp=self.log_exp[i], - possible_awakening_flag=self.possible_awakening_flag, - awakening_stage=self.awakening_stage, - awakening_exp=self.awakening_exp, - skill_slot_correction_value=self.skill_slot_correction_value, - last_set_skill_slot1_skill_id=self.last_set_skill_slot1_skill_id[i], - last_set_skill_slot2_skill_id=self.last_set_skill_slot2_skill_id[i], - last_set_skill_slot3_skill_id=self.last_set_skill_slot3_skill_id[i], - last_set_skill_slot4_skill_id=self.last_set_skill_slot4_skill_id[i], - last_set_skill_slot5_skill_id=self.last_set_skill_slot5_skill_id[i], - property1_property_id=self.property1_property_id, - property1_value1=self.property1_value1, - property1_value2=self.property1_value2, - property2_property_id=self.property2_property_id, - property2_value1=self.property2_value1, - property2_value2=self.property2_value2, - property3_property_id=self.property3_property_id, - property3_value1=self.property3_value1, - property3_value2=self.property3_value2, - property4_property_id=self.property4_property_id, - property4_value1=self.property4_value1, - property4_value2=self.property4_value2, - converted_card_num=self.converted_card_num, - shop_purchase_flag=self.shop_purchase_flag, - protect_flag=self.protect_flag, - get_date_size=len(self.get_date) * 2, - get_date=[ord(x) for x in self.get_date], - - ) - - resp_data.hero_log_user_data_list.append(hero_data) - - resp_data["hero_log_user_data_list_size"] = len(resp_data.hero_log_user_data_list) - - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetEquipmentUserDataListRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.user_id = decode_str(data, 0)[0] + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.hero_log_user_data_list) class SaoGetEquipmentUserDataListResponse(SaoBaseResponse): - def __init__(self, cmd, equipment_data) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_EQUIPMENT_USER_DATA_LIST_RESPONSE) self.result = 1 - - self.user_equipment_id = [] - self.enhancement_value = [] - self.max_enhancement_value_extended_num = [] - self.enhancement_exp = [] - self.awakening_stage = [] - self.awakening_exp = [] - self.possible_awakening_flag = [] - equipment_level = 0 - - for i in range(len(equipment_data)): - - # Calculate level based off experience and the CSV list - with open(r'titles/sao/data/EquipmentLevel.csv') as csv_file: - csv_reader = csv.reader(csv_file, delimiter=',') - line_count = 0 - data = [] - rowf = False - for row in csv_reader: - if rowf==False: - rowf=True - else: - data.append(row) - - exp = equipment_data[i][4] - - for e in range(0,len(data)): - if exp>=int(data[e][1]) and exp bytes: - - equipment_user_data_list_struct = Struct( - "user_equipment_id_size" / Int32ub, # big endian - "user_equipment_id" / Int16ul[9], #string - "equipment_id" / Int32ub, #int - "enhancement_value" / Int16ub, #short - "max_enhancement_value_extended_num" / Int16ub, #short - "enhancement_exp" / Int32ub, #int - "possible_awakening_flag" / Int8ul, # result is either 0 or 1 - "awakening_stage" / Int16ub, #short - "awakening_exp" / Int32ub, #int - "property1_property_id" / Int32ub, - "property1_value1" / Int32ub, - "property1_value2" / Int32ub, - "property2_property_id" / Int32ub, - "property2_value1" / Int32ub, - "property2_value2" / Int32ub, - "property3_property_id" / Int32ub, - "property3_value1" / Int32ub, - "property3_value2" / Int32ub, - "property4_property_id" / Int32ub, - "property4_value1" / Int32ub, - "property4_value2" / Int32ub, - "converted_card_num" / Int16ub, - "shop_purchase_flag" / Int8ul, # result is either 0 or 1 - "protect_flag" / Int8ul, # result is either 0 or 1 - "get_date_size" / Int32ub, # big endian - "get_date" / Int16ul[len(self.get_date)], - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "equipment_user_data_list_size" / Rebuild(Int32ub, len_(this.equipment_user_data_list)), # big endian - "equipment_user_data_list" / Array(this.equipment_user_data_list_size, equipment_user_data_list_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - equipment_user_data_list_size=0, - equipment_user_data_list=[], - ))) - - for i in range(len(self.equipment_id)): - equipment_data = dict( - user_equipment_id_size=len(self.user_equipment_id[i]) * 2, - user_equipment_id=[ord(x) for x in self.user_equipment_id[i]], - equipment_id=self.equipment_id[i], - enhancement_value=self.enhancement_value[i], - max_enhancement_value_extended_num=self.max_enhancement_value_extended_num[i], - enhancement_exp=self.enhancement_exp[i], - possible_awakening_flag=self.possible_awakening_flag[i], - awakening_stage=self.awakening_stage[i], - awakening_exp=self.awakening_exp[i], - property1_property_id=self.property1_property_id, - property1_value1=self.property1_value1, - property1_value2=self.property1_value2, - property2_property_id=self.property2_property_id, - property2_value1=self.property2_value1, - property2_value2=self.property2_value2, - property3_property_id=self.property3_property_id, - property3_value1=self.property3_value1, - property3_value2=self.property3_value2, - property4_property_id=self.property4_property_id, - property4_value1=self.property4_value1, - property4_value2=self.property4_value2, - converted_card_num=self.converted_card_num, - shop_purchase_flag=self.shop_purchase_flag, - protect_flag=self.protect_flag, - get_date_size=len(self.get_date) * 2, - get_date=[ord(x) for x in self.get_date], - - ) - - resp_data.equipment_user_data_list.append(equipment_data) - - resp_data["equipment_user_data_list_size"] = len(resp_data.equipment_user_data_list) - - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetItemUserDataListRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.user_id = decode_str(data, 0)[0] + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.equipment_user_data_list) class SaoGetItemUserDataListResponse(SaoBaseResponse): - def __init__(self, cmd, item_data) -> None: - super().__init__(cmd) + def __init__(self, item_data: List[Dict] = []) -> None: + super().__init__(GameconnectCmd.GET_ITEM_USER_DATA_LIST_RESPONSE) self.result = 1 + self.item_user_data_list: List[ItemUserData] = [] - self.user_item_id = [] - - for i in range(len(item_data)): - self.user_item_id.append(item_data[i][2]) - - # item_user_data_list - self.user_item_id = list(map(str,self.user_item_id)) #str - self.item_id = list(map(int,self.user_item_id)) #int - self.protect_flag = 0 #byte - self.get_date = "20230101120000" #str + if item_data: + for item in item_data: + self.item_user_data_list.append(ItemUserData.from_args(item)) def make(self) -> bytes: - #new stuff - - item_user_data_list_struct = Struct( - "user_item_id_size" / Int32ub, # big endian - "user_item_id" / Int16ul[6], #string but this will not work with 10000 IDs... only with 6 digits - "item_id" / Int32ub, #int - "protect_flag" / Int8ul, # result is either 0 or 1 - "get_date_size" / Int32ub, # big endian - "get_date" / Int16ul[len(self.get_date)], - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "item_user_data_list_size" / Rebuild(Int32ub, len_(this.item_user_data_list)), # big endian - "item_user_data_list" / Array(this.item_user_data_list_size, item_user_data_list_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - item_user_data_list_size=0, - item_user_data_list=[], - ))) - - for i in range(len(self.item_id)): - item_data = dict( - user_item_id_size=len(self.user_item_id[i]) * 2, - user_item_id=[ord(x) for x in self.user_item_id[i]], - item_id=self.item_id[i], - protect_flag=self.protect_flag, - get_date_size=len(self.get_date) * 2, - get_date=[ord(x) for x in self.get_date], - - ) - - resp_data.item_user_data_list.append(item_data) - - resp_data["item_user_data_list_size"] = len(resp_data.item_user_data_list) - - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetSupportLogUserDataListRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.item_user_data_list) class SaoGetSupportLogUserDataListResponse(SaoBaseResponse): - def __init__(self, cmd, supportIdsData) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_SUPPORT_LOG_USER_DATA_LIST_RESPONSE) self.result = 1 + self.support_log_user_data_list: List[SupportLogUserData] = [] - # support_log_user_data_list - self.user_support_log_id = list(map(str,supportIdsData)) #str - self.support_log_id = supportIdsData #int - self.possible_awakening_flag = 0 - self.awakening_stage = 0 - self.awakening_exp = 0 - self.converted_card_num = 0 - self.shop_purchase_flag = 0 - self.protect_flag = 0 #byte - self.get_date = "20230101120000" #str - def make(self) -> bytes: - support_log_user_data_list_struct = Struct( - "user_support_log_id_size" / Int32ub, # big endian - "user_support_log_id" / Int16ul[9], - "support_log_id" / Int32ub, #int - "possible_awakening_flag" / Int8ul, # result is either 0 or 1 - "awakening_stage" / Int16ub, #short - "awakening_exp" / Int32ub, # int - "converted_card_num" / Int16ub, #short - "shop_purchase_flag" / Int8ul, # result is either 0 or 1 - "protect_flag" / Int8ul, # result is either 0 or 1 - "get_date_size" / Int32ub, # big endian - "get_date" / Int16ul[len(self.get_date)], - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "support_log_user_data_list_size" / Rebuild(Int32ub, len_(this.support_log_user_data_list)), # big endian - "support_log_user_data_list" / Array(this.support_log_user_data_list_size, support_log_user_data_list_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - support_log_user_data_list_size=0, - support_log_user_data_list=[], - ))) - - for i in range(len(self.support_log_id)): - support_data = dict( - user_support_log_id_size=len(self.user_support_log_id[i]) * 2, - user_support_log_id=[ord(x) for x in self.user_support_log_id[i]], - support_log_id=self.support_log_id[i], - possible_awakening_flag=self.possible_awakening_flag, - awakening_stage=self.awakening_stage, - awakening_exp=self.awakening_exp, - converted_card_num=self.converted_card_num, - shop_purchase_flag=self.shop_purchase_flag, - protect_flag=self.protect_flag, - get_date_size=len(self.get_date) * 2, - get_date=[ord(x) for x in self.get_date], - - ) - - resp_data.support_log_user_data_list.append(support_data) - - resp_data["support_log_user_data_list_size"] = len(resp_data.support_log_user_data_list) - - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetTitleUserDataListRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.support_log_user_data_list) class SaoGetTitleUserDataListResponse(SaoBaseResponse): - def __init__(self, cmd, titleIdsData) -> None: - super().__init__(cmd) + def __init__(self, user_id: str, titles: List[int]) -> None: + super().__init__(GameconnectCmd.GET_TITLE_USER_DATA_LIST_RESPONSE) self.result = 1 + self.title_user_data_list: List[TitleUserData] = [] + + if titles: + for x in titles: + self.title_user_data_list.append(TitleUserData.from_args(user_id + str(x), x)) - # title_user_data_list - self.user_title_id = list(map(str,titleIdsData)) #str - self.title_id = titleIdsData #int - def make(self) -> bytes: - title_user_data_list_struct = Struct( - "user_title_id_size" / Int32ub, # big endian - "user_title_id" / Int16ul[6], #string - "title_id" / Int32ub, #int - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "title_user_data_list_size" / Rebuild(Int32ub, len_(this.title_user_data_list)), # big endian - "title_user_data_list" / Array(this.title_user_data_list_size, title_user_data_list_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - title_user_data_list_size=0, - title_user_data_list=[], - ))) - - for i in range(len(self.title_id)): - title_data = dict( - user_title_id_size=len(self.user_title_id[i]) * 2, - user_title_id=[ord(x) for x in self.user_title_id[i]], - title_id=self.title_id[i], - ) - - resp_data.title_user_data_list.append(title_data) - - resp_data["title_user_data_list_size"] = len(resp_data.title_user_data_list) - - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetEpisodeAppendDataListRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.user_id = decode_str(data, 0)[0] + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.title_user_data_list) class SaoGetEpisodeAppendDataListResponse(SaoBaseResponse): - def __init__(self, cmd, profile_data) -> None: - super().__init__(cmd) - self.length = None + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_EPISODE_APPEND_DATA_LIST_RESPONSE) self.result = 1 - - self.user_episode_append_id_list = ["10001", "10002", "10003", "10004", "10005"] - self.user_id_list = [str(profile_data["user"]), str(profile_data["user"]), str(profile_data["user"]), str(profile_data["user"]), str(profile_data["user"])] - self.episode_append_id_list = [10001, 10002, 10003, 10004, 10005] - self.own_num_list = [3, 3, 3, 3 ,3] + self.episode_append_data_list: List[EpisodeAppendUserData] = [] def make(self) -> bytes: - episode_data_struct = Struct( - "user_episode_append_id_size" / Rebuild(Int32ub, len_(this.user_episode_append_id) * 2), # calculates the length of the user_episode_append_id - "user_episode_append_id" / PaddedString(this.user_episode_append_id_size, "utf_16_le"), # user_episode_append_id is a (zero) padded string - "user_id_size" / Rebuild(Int32ub, len_(this.user_id) * 2), # calculates the length of the user_id - "user_id" / PaddedString(this.user_id_size, "utf_16_le"), # user_id is a (zero) padded string - "episode_append_id" / Int32ub, - "own_num" / Int32ub, - ) + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.episode_append_data_list) - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "episode_append_data_list_size" / Rebuild(Int32ub, len_(this.episode_append_data_list)), # big endian - "episode_append_data_list" / Array(this.episode_append_data_list_size, episode_data_struct), - ) - - # really dump to parse the build resp, but that creates a new object - # and is nicer to twork with - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - episode_append_data_list_size=0, - episode_append_data_list=[], - ))) - - if len(self.user_episode_append_id_list) != len(self.user_id_list) != len(self.episode_append_id_list) != len(self.own_num_list): - raise ValueError("all lists must be of the same length") - - for i in range(len(self.user_id_list)): - # add the episode_data_struct to the resp_struct.episode_append_data_list - resp_data.episode_append_data_list.append(dict( - user_episode_append_id=self.user_episode_append_id_list[i], - user_id=self.user_id_list[i], - episode_append_id=self.episode_append_id_list[i], - own_num=self.own_num_list[i], - )) - - resp_data["episode_append_data_list_size"] = len(resp_data.episode_append_data_list) - - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetPartyDataListRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.user_id = decode_str(data, 0)[0] - -class SaoGetPartyDataListResponse(SaoBaseResponse): # Default party - def __init__(self, cmd, hero1_data, hero2_data, hero3_data) -> None: - super().__init__(cmd) - +class SaoGetEventItemDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_EVENT_ITEM_DATA_LIST_RESPONSE) self.result = 1 - self.party_data_list_size = 1 # Number of arrays - - self.user_party_id = "0" - self.team_no = 0 - self.party_team_data_list_size = 3 # Number of arrays - - self.user_party_team_id_1 = "0" - self.arrangement_num_1 = 0 - self.user_hero_log_id_1 = str(hero1_data[2]) - self.main_weapon_user_equipment_id_1 = str(hero1_data[5]) - self.sub_equipment_user_equipment_id_1 = str(hero1_data[6]) - self.skill_slot1_skill_id_1 = hero1_data[7] - self.skill_slot2_skill_id_1 = hero1_data[8] - self.skill_slot3_skill_id_1 = hero1_data[9] - self.skill_slot4_skill_id_1 = hero1_data[10] - self.skill_slot5_skill_id_1 = hero1_data[11] - - self.user_party_team_id_2 = "0" - self.arrangement_num_2 = 0 - self.user_hero_log_id_2 = str(hero2_data[2]) - self.main_weapon_user_equipment_id_2 = str(hero2_data[5]) - self.sub_equipment_user_equipment_id_2 = str(hero2_data[6]) - self.skill_slot1_skill_id_2 = hero2_data[7] - self.skill_slot2_skill_id_2 = hero2_data[8] - self.skill_slot3_skill_id_2 = hero2_data[9] - self.skill_slot4_skill_id_2 = hero2_data[10] - self.skill_slot5_skill_id_2 = hero2_data[11] - - self.user_party_team_id_3 = "0" - self.arrangement_num_3 = 0 - self.user_hero_log_id_3 = str(hero3_data[2]) - self.main_weapon_user_equipment_id_3 = str(hero3_data[5]) - self.sub_equipment_user_equipment_id_3 = str(hero3_data[6]) - self.skill_slot1_skill_id_3 = hero3_data[7] - self.skill_slot2_skill_id_3 = hero3_data[8] - self.skill_slot3_skill_id_3 = hero3_data[9] - self.skill_slot4_skill_id_3 = hero3_data[10] - self.skill_slot5_skill_id_3 = hero3_data[11] + self.event_item_data_list: List[EventItemUserData] = [] def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "party_data_list_size" / Int32ub, # big endian + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.event_item_data_list) - "user_party_id_size" / Int32ub, # big endian - "user_party_id" / Int16ul[len(self.user_party_id)], - "team_no" / Int8ul, # result is either 0 or 1 - "party_team_data_list_size" / Int32ub, # big endian - - "user_party_team_id_1_size" / Int32ub, # big endian - "user_party_team_id_1" / Int16ul[len(self.user_party_team_id_1)], - "arrangement_num_1" / Int8ul, # big endian - "user_hero_log_id_1_size" / Int32ub, # big endian - "user_hero_log_id_1" / Int16ul[len(self.user_hero_log_id_1)], - "main_weapon_user_equipment_id_1_size" / Int32ub, # big endian - "main_weapon_user_equipment_id_1" / Int16ul[len(self.main_weapon_user_equipment_id_1)], - "sub_equipment_user_equipment_id_1_size" / Int32ub, # big endian - "sub_equipment_user_equipment_id_1" / Int16ul[len(self.sub_equipment_user_equipment_id_1)], - "skill_slot1_skill_id_1" / Int32ub, - "skill_slot2_skill_id_1" / Int32ub, - "skill_slot3_skill_id_1" / Int32ub, - "skill_slot4_skill_id_1" / Int32ub, - "skill_slot5_skill_id_1" / Int32ub, - - "user_party_team_id_2_size" / Int32ub, # big endian - "user_party_team_id_2" / Int16ul[len(self.user_party_team_id_2)], - "arrangement_num_2" / Int8ul, # result is either 0 or 1 - "user_hero_log_id_2_size" / Int32ub, # big endian - "user_hero_log_id_2" / Int16ul[len(self.user_hero_log_id_2)], - "main_weapon_user_equipment_id_2_size" / Int32ub, # big endian - "main_weapon_user_equipment_id_2" / Int16ul[len(self.main_weapon_user_equipment_id_2)], - "sub_equipment_user_equipment_id_2_size" / Int32ub, # big endian - "sub_equipment_user_equipment_id_2" / Int16ul[len(self.sub_equipment_user_equipment_id_2)], - "skill_slot1_skill_id_2" / Int32ub, - "skill_slot2_skill_id_2" / Int32ub, - "skill_slot3_skill_id_2" / Int32ub, - "skill_slot4_skill_id_2" / Int32ub, - "skill_slot5_skill_id_2" / Int32ub, - - "user_party_team_id_3_size" / Int32ub, # big endian - "user_party_team_id_3" / Int16ul[len(self.user_party_team_id_3)], - "arrangement_num_3" / Int8ul, # result is either 0 or 1 - "user_hero_log_id_3_size" / Int32ub, # big endian - "user_hero_log_id_3" / Int16ul[len(self.user_hero_log_id_3)], - "main_weapon_user_equipment_id_3_size" / Int32ub, # big endian - "main_weapon_user_equipment_id_3" / Int16ul[len(self.main_weapon_user_equipment_id_3)], - "sub_equipment_user_equipment_id_3_size" / Int32ub, # big endian - "sub_equipment_user_equipment_id_3" / Int16ul[len(self.sub_equipment_user_equipment_id_3)], - "skill_slot1_skill_id_3" / Int32ub, - "skill_slot2_skill_id_3" / Int32ub, - "skill_slot3_skill_id_3" / Int32ub, - "skill_slot4_skill_id_3" / Int32ub, - "skill_slot5_skill_id_3" / Int32ub, - - ) - - resp_data = resp_struct.build(dict( - result=self.result, - party_data_list_size=self.party_data_list_size, - - user_party_id_size=len(self.user_party_id) * 2, - user_party_id=[ord(x) for x in self.user_party_id], - team_no=self.team_no, - party_team_data_list_size=self.party_team_data_list_size, - - user_party_team_id_1_size=len(self.user_party_team_id_1) * 2, - user_party_team_id_1=[ord(x) for x in self.user_party_team_id_1], - arrangement_num_1=self.arrangement_num_1, - user_hero_log_id_1_size=len(self.user_hero_log_id_1) * 2, - user_hero_log_id_1=[ord(x) for x in self.user_hero_log_id_1], - main_weapon_user_equipment_id_1_size=len(self.main_weapon_user_equipment_id_1) * 2, - main_weapon_user_equipment_id_1=[ord(x) for x in self.main_weapon_user_equipment_id_1], - sub_equipment_user_equipment_id_1_size=len(self.sub_equipment_user_equipment_id_1) * 2, - sub_equipment_user_equipment_id_1=[ord(x) for x in self.sub_equipment_user_equipment_id_1], - skill_slot1_skill_id_1=self.skill_slot1_skill_id_1, - skill_slot2_skill_id_1=self.skill_slot2_skill_id_1, - skill_slot3_skill_id_1=self.skill_slot3_skill_id_1, - skill_slot4_skill_id_1=self.skill_slot4_skill_id_1, - skill_slot5_skill_id_1=self.skill_slot5_skill_id_1, - - user_party_team_id_2_size=len(self.user_party_team_id_2) * 2, - user_party_team_id_2=[ord(x) for x in self.user_party_team_id_2], - arrangement_num_2=self.arrangement_num_2, - user_hero_log_id_2_size=len(self.user_hero_log_id_2) * 2, - user_hero_log_id_2=[ord(x) for x in self.user_hero_log_id_2], - main_weapon_user_equipment_id_2_size=len(self.main_weapon_user_equipment_id_2) * 2, - main_weapon_user_equipment_id_2=[ord(x) for x in self.main_weapon_user_equipment_id_2], - sub_equipment_user_equipment_id_2_size=len(self.sub_equipment_user_equipment_id_2) * 2, - sub_equipment_user_equipment_id_2=[ord(x) for x in self.sub_equipment_user_equipment_id_2], - skill_slot1_skill_id_2=self.skill_slot1_skill_id_2, - skill_slot2_skill_id_2=self.skill_slot2_skill_id_2, - skill_slot3_skill_id_2=self.skill_slot3_skill_id_2, - skill_slot4_skill_id_2=self.skill_slot4_skill_id_2, - skill_slot5_skill_id_2=self.skill_slot5_skill_id_2, - - user_party_team_id_3_size=len(self.user_party_team_id_3) * 2, - user_party_team_id_3=[ord(x) for x in self.user_party_team_id_3], - arrangement_num_3=self.arrangement_num_3, - user_hero_log_id_3_size=len(self.user_hero_log_id_3) * 2, - user_hero_log_id_3=[ord(x) for x in self.user_hero_log_id_3], - main_weapon_user_equipment_id_3_size=len(self.main_weapon_user_equipment_id_3) * 2, - main_weapon_user_equipment_id_3=[ord(x) for x in self.main_weapon_user_equipment_id_3], - sub_equipment_user_equipment_id_3_size=len(self.sub_equipment_user_equipment_id_3) * 2, - sub_equipment_user_equipment_id_3=[ord(x) for x in self.sub_equipment_user_equipment_id_3], - skill_slot1_skill_id_3=self.skill_slot1_skill_id_3, - skill_slot2_skill_id_3=self.skill_slot2_skill_id_3, - skill_slot3_skill_id_3=self.skill_slot3_skill_id_3, - skill_slot4_skill_id_3=self.skill_slot4_skill_id_3, - skill_slot5_skill_id_3=self.skill_slot5_skill_id_3, - )) - - self.length = len(resp_data) - return super().make() + resp_data +class SaoGetGashaMedalUserDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_GASHA_MEDAL_USER_DATA_LIST_RESPONSE) + self.result = 1 + self.data_list: List[GashaMedalUserData] = [] -class SaoGetQuestScenePrevScanProfileCardRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetPartyDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_PARTY_DATA_LIST_RESPONSE) + self.result = 1 + self.party_data_list: List[PartyData] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.party_data_list) + +class SaoGetSupportLogPartyDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_SUPPORT_LOG_PARTY_DATA_LIST_RESPONSE) + self.result = 1 + self.support_log_party_data_list: List[SupportLogPartyData] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.support_log_party_data_list) class SaoGetQuestScenePrevScanProfileCardResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_QUEST_SCENE_PREV_SCAN_PROFILE_CARD_RESPONSE) self.result = 1 - self.profile_card_data = 1 # number of arrays - - self.profile_card_code = "" - self.nick_name = "" - + self.profile_card_data: List[ReadProfileCardData] = [] + def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "profile_card_data" / Int32ub, # big endian - - "profile_card_code_size" / Int32ub, # big endian - "profile_card_code" / Int16ul[len(self.profile_card_code)], - "nick_name_size" / Int32ub, # big endian - "nick_name" / Int16ul[len(self.nick_name)], - "rank_num" / Int16ub, #short - "setting_title_id" / Int32ub, # int - "skill_id" / Int16ub, # short - "hero_log_hero_log_id" / Int32ub, # int - "hero_log_log_level" / Int16ub, # short - "hero_log_awakening_stage" / Int16ub, # short - "hero_log_property1_property_id" / Int32ub, # int - "hero_log_property1_value1" / Int32ub, # int - "hero_log_property1_value2" / Int32ub, # int - "hero_log_property2_property_id" / Int32ub, # int - "hero_log_property2_value1" / Int32ub, # int - "hero_log_property2_value2" / Int32ub, # int - "hero_log_property3_property_id" / Int32ub, # int - "hero_log_property3_value1" / Int32ub, # int - "hero_log_property3_value2" / Int32ub, # int - "hero_log_property4_property_id" / Int32ub, # int - "hero_log_property4_value1" / Int32ub, # int - "hero_log_property4_value2" / Int32ub, # int - "main_weapon_equipment_id" / Int32ub, # int - "main_weapon_enhancement_value" / Int16ub, # short - "main_weapon_awakening_stage" / Int16ub, # short - "main_weapon_property1_property_id" / Int32ub, # int - "main_weapon_property1_value1" / Int32ub, # int - "main_weapon_property1_value2" / Int32ub, # int - "main_weapon_property2_property_id" / Int32ub, # int - "main_weapon_property2_value1" / Int32ub, # int - "main_weapon_property2_value2" / Int32ub, # int - "main_weapon_property3_property_id" / Int32ub, # int - "main_weapon_property3_value1" / Int32ub, # int - "main_weapon_property3_value2" / Int32ub, # int - "main_weapon_property4_property_id" / Int32ub, # int - "main_weapon_property4_value1" / Int32ub, # int - "main_weapon_property4_value2" / Int32ub, # int - "sub_equipment_equipment_id" / Int32ub, # int - "sub_equipment_enhancement_value" / Int16ub, # short - "sub_equipment_awakening_stage" / Int16ub, # short - "sub_equipment_property1_property_id" / Int32ub, # int - "sub_equipment_property1_value1" / Int32ub, # int - "sub_equipment_property1_value2" / Int32ub, # int - "sub_equipment_property2_property_id" / Int32ub, # int - "sub_equipment_property2_value1" / Int32ub, # int - "sub_equipment_property2_value2" / Int32ub, # int - "sub_equipment_property3_property_id" / Int32ub, # int - "sub_equipment_property3_value1" / Int32ub, # int - "sub_equipment_property3_value2" / Int32ub, # int - "sub_equipment_property4_property_id" / Int32ub, # int - "sub_equipment_property4_value1" / Int32ub, # int - "sub_equipment_property4_value2" / Int32ub, # int - "holographic_flag" / Int8ul, # result is either 0 or 1 - ) - - resp_data = resp_struct.build(dict( - result=self.result, - profile_card_data=self.profile_card_data, - - profile_card_code_size=len(self.profile_card_code) * 2, - profile_card_code=[ord(x) for x in self.profile_card_code], - nick_name_size=len(self.nick_name) * 2, - nick_name=[ord(x) for x in self.nick_name], - rank_num=0, - setting_title_id=0, - skill_id=0, - hero_log_hero_log_id=0, - hero_log_log_level=0, - hero_log_awakening_stage=0, - hero_log_property1_property_id=0, - hero_log_property1_value1=0, - hero_log_property1_value2=0, - hero_log_property2_property_id=0, - hero_log_property2_value1=0, - hero_log_property2_value2=0, - hero_log_property3_property_id=0, - hero_log_property3_value1=0, - hero_log_property3_value2=0, - hero_log_property4_property_id=0, - hero_log_property4_value1=0, - hero_log_property4_value2=0, - main_weapon_equipment_id=0, - main_weapon_enhancement_value=0, - main_weapon_awakening_stage=0, - main_weapon_property1_property_id=0, - main_weapon_property1_value1=0, - main_weapon_property1_value2=0, - main_weapon_property2_property_id=0, - main_weapon_property2_value1=0, - main_weapon_property2_value2=0, - main_weapon_property3_property_id=0, - main_weapon_property3_value1=0, - main_weapon_property3_value2=0, - main_weapon_property4_property_id=0, - main_weapon_property4_value1=0, - main_weapon_property4_value2=0, - sub_equipment_equipment_id=0, - sub_equipment_enhancement_value=0, - sub_equipment_awakening_stage=0, - sub_equipment_property1_property_id=0, - sub_equipment_property1_value1=0, - sub_equipment_property1_value2=0, - sub_equipment_property2_property_id=0, - sub_equipment_property2_value1=0, - sub_equipment_property2_value2=0, - sub_equipment_property3_property_id=0, - sub_equipment_property3_value1=0, - sub_equipment_property3_value2=0, - sub_equipment_property4_property_id=0, - sub_equipment_property4_value1=0, - sub_equipment_property4_value2=0, - holographic_flag=0, - - )) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetResourcePathInfoRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.profile_card_data) class SaoGetResourcePathInfoResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self, base_url: str) -> None: + super().__init__(GameconnectCmd.GET_RESOURCE_PATH_INFO_RESPONSE) self.result = 1 - self.resource_base_url = "http://localhost:9000/SDEW/100/" - self.gasha_base_dir = "a" - self.ad_base_dir = "b" - self.event_base_dir = "c" + self.resource_base_url = base_url + self.gasha_base_dir = "gasha" + self.ad_base_dir = "ad" + self.event_base_dir = "event" def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "resource_base_url_size" / Int32ub, # big endian - "resource_base_url" / Int16ul[len(self.resource_base_url)], - "gasha_base_dir_size" / Int32ub, # big endian - "gasha_base_dir" / Int16ul[len(self.gasha_base_dir)], - "ad_base_dir_size" / Int32ub, # big endian - "ad_base_dir" / Int16ul[len(self.ad_base_dir)], - "event_base_dir_size" / Int32ub, # big endian - "event_base_dir" / Int16ul[len(self.event_base_dir)], - ) + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.resource_base_url) \ + + encode_str(self.gasha_base_dir) \ + + encode_str(self.ad_base_dir) \ + + encode_str(self.event_base_dir) - resp_data = resp_struct.build(dict( - result=self.result, - resource_base_url_size=len(self.resource_base_url) * 2, - resource_base_url=[ord(x) for x in self.resource_base_url], - gasha_base_dir_size=len(self.gasha_base_dir) * 2, - gasha_base_dir=[ord(x) for x in self.gasha_base_dir], - ad_base_dir_size=len(self.ad_base_dir) * 2, - ad_base_dir=[ord(x) for x in self.ad_base_dir], - event_base_dir_size=len(self.event_base_dir) * 2, - event_base_dir=[ord(x) for x in self.event_base_dir], - )) +class SaoValidationErrorNotificationRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off - self.length = len(resp_data) - return super().make() + resp_data + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + + self.net_id, new_off = decode_str(data, off) + off += new_off + + self.place_id, new_off = decode_str(data, off) + off += new_off + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.store_name, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.send_protocol_name, new_off = decode_str(data, off) + off += new_off + + self.send_data_to_fraud_value, new_off = decode_str(data, off) + off += new_off + + self.send_data_to_modification_value, new_off = decode_str(data, off) + off += new_off + +class SaoGetBeginnerMissionUserDataRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + +class SaoGetBeginnerMissionUserDataResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_BEGINNER_MISSION_USER_DATA_RESPONSE) + self.result = 1 + self.data: List[BeginnerMissionUserData] = [BeginnerMissionUserData.from_args(datetime.now())] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data) + +class SaoMatchingErrorNotificationRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + + self.net_id, new_off = decode_str(data, off) + off += new_off + + self.place_id, new_off = decode_str(data, off) + off += new_off + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.store_name, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.matching_error_data_list: List[MatchingErrorData] + self.matching_error_data_list, new_off = decode_arr_cls(data, off, MatchingErrorData) + +class SaoPowerCuttingReturnNotification(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.cabinet_type = decode_byte(data, off) + off += BYTE_OFF + + self.net_id, new_off = decode_str(data, off) + off += new_off + + self.place_id, new_off = decode_str(data, off) + off += new_off + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.store_name, new_off = decode_str(data, off) + off += new_off + + self.serial_no, new_off = decode_str(data, off) + off += new_off + + self.last_act_type = decode_byte(data, off) + off += BYTE_OFF + + self.remaining_ticket_num = decode_short(data, off) + off += SHORT_OFF + + self.remaining_credit_num = decode_short(data, off) + off += SHORT_OFF class SaoEpisodePlayStartRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) off = 0 - ticket_id = decode_str(data, off) - self.ticket_id = ticket_id[0] - off += ticket_id[1] + self.ticket_id, new_off = decode_str(data, off) + off += new_off - user_id = decode_str(data, off) - self.user_id = user_id[0] - off += user_id[1] + self.user_id, new_off = decode_str(data, off) + off += new_off self.episode_id = decode_int(data, off) off += INT_OFF @@ -1500,183 +927,60 @@ class SaoEpisodePlayStartRequest(SaoBaseRequest): self.play_mode = decode_byte(data, off) off += BYTE_OFF - self.play_start_request_data_count = decode_int(data, off) - off += INT_OFF + self.play_start_request_data: List[QuestScenePlayStartRequestData] = [] + self.play_start_request_data, new_off = decode_arr_cls(data, off, QuestScenePlayStartRequestData) + off += new_off - self.play_start_request_data: List[PlayStartRequestData] = [] - for _ in range(self.play_start_request_data_count): - tmp = PlayStartRequestData(data, off) - self.play_start_request_data.append(tmp) - off += tmp.get_size() - - self.multi_play_start_request_data_count = decode_int(data, off) - off += INT_OFF - - self.multi_play_start_request_data: List[MultiPlayStartRequestData] = [] - for _ in range(self.multi_play_start_request_data_count): - tmp = MultiPlayStartRequestData(data, off) - off += tmp.get_size() - self.multi_play_start_request_data.append(tmp) + self.multi_play_start_request_data: List[QuestSceneMultiPlayStartRequestData] = [] + self.multi_play_start_request_data, new_off = decode_arr_cls(data, off, QuestSceneMultiPlayStartRequestData) + off += new_off class SaoEpisodePlayStartResponse(SaoBaseResponse): - def __init__(self, cmd, profile_data) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.EPISODE_PLAY_START_RESPONSE) self.result = 1 - self.play_start_response_data_size = 1 # Number of arrays (minimum 1 mandatory) - self.multi_play_start_response_data_size = 0 # Number of arrays (set 0 due to single play) + self.play_start_response_data: List[QuestScenePlayStartResponseData] = [] + self.multi_play_start_response_data: List[QuestSceneMultiPlayStartResponseData] = [] - self.appearance_player_trace_data_list_size = 1 - - self.user_quest_scene_player_trace_id = "1003" - self.nick_name = profile_data["nick_name"] - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "play_start_response_data_size" / Int32ub, - "multi_play_start_response_data_size" / Int32ub, - - "appearance_player_trace_data_list_size" / Int32ub, - - "user_quest_scene_player_trace_id_size" / Int32ub, # big endian - "user_quest_scene_player_trace_id" / Int16ul[len(self.user_quest_scene_player_trace_id)], - "nick_name_size" / Int32ub, # big endian - "nick_name" / Int16ul[len(self.nick_name)], - ) - - resp_data = resp_struct.build(dict( - result=self.result, - play_start_response_data_size=self.play_start_response_data_size, - multi_play_start_response_data_size=self.multi_play_start_response_data_size, - - appearance_player_trace_data_list_size=self.appearance_player_trace_data_list_size, - - user_quest_scene_player_trace_id_size=len(self.user_quest_scene_player_trace_id) * 2, - user_quest_scene_player_trace_id=[ord(x) for x in self.user_quest_scene_player_trace_id], - nick_name_size=len(self.nick_name) * 2, - nick_name=[ord(x) for x in self.nick_name], - )) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.play_start_response_data) \ + + encode_arr_cls(self.multi_play_start_response_data) class SaoEpisodePlayEndRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) off = 0 - ticket_id = decode_str(data, off) - self.ticket_id = ticket_id[0] - off += ticket_id[1] + self.ticket_id, new_off = decode_str(data, off) + off += new_off - user_id = decode_str(data, off) - self.user_id = user_id[0] - off += user_id[1] + self.user_id, new_off = decode_str(data, off) + off += new_off self.episode_id = decode_int(data, off) off += INT_OFF - self.play_end_request_data_count = decode_int(data, off) - off += INT_OFF + self.play_end_request_data: List[QuestScenePlayEndRequestData] = [] + self.play_end_request_data, new_off = decode_arr_cls(data, off, QuestScenePlayEndRequestData) + off += new_off - self.play_end_request_data_list: List[PlayEndRequestData] = [] - for _ in range(self.play_end_request_data_count): - tmp = PlayEndRequestData(data, off) - off += tmp.get_size() - self.play_end_request_data_list.append(tmp) - - self.multi_play_end_request_data_count = decode_int(data, off) - off += INT_OFF - - self.multi_play_end_request_data_list: List[MultiPlayEndRequestData] = [] - for _ in range(self.multi_play_end_request_data_count): - tmp = MultiPlayEndRequestData(data, off) - off += tmp.get_size() - self.multi_play_end_request_data_list.append(tmp) + self.multi_play_end_request_data: List[QuestSceneMultiPlayEndRequestData] = [] + self.multi_play_end_request_data, new_off = decode_arr_cls(data, off, QuestSceneMultiPlayEndRequestData) + off += new_off class SaoEpisodePlayEndResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.EPISODE_PLAY_END_RESPONSE) self.result = 1 - self.play_end_response_data_size = 1 # Number of arrays - self.multi_play_end_response_data_size = 1 # Unused on solo play + self.play_end_response_data: List[QuestScenePlayEndResponseData] = [QuestScenePlayEndResponseData.from_args()] + self.multi_play_end_response_data: List[QuestSceneMultiPlayEndResponseData] = [QuestSceneMultiPlayEndResponseData.from_args()] - self.dummy_1 = 0 - self.dummy_2 = 0 - self.dummy_3 = 0 - - self.rarity_up_occurrence_flag = 0 - self.adventure_ex_area_occurrences_flag = 0 - self.ex_bonus_data_list_size = 1 # Number of arrays - self.play_end_player_trace_reward_data_list_size = 0 # Number of arrays - - self.ex_bonus_table_id = 0 # ExBonusTable.csv values, dont care for now - self.achievement_status = 1 - - self.common_reward_data_size = 1 # Number of arrays - - self.common_reward_type = 0 # dummy values from 2,101000000,1 from RewardTable.csv - self.common_reward_id = 0 - self.common_reward_num = 0 - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "play_end_response_data_size" / Int32ub, # big endian - - "rarity_up_occurrence_flag" / Int8ul, # result is either 0 or 1 - "adventure_ex_area_occurrences_flag" / Int8ul, # result is either 0 or 1 - "ex_bonus_data_list_size" / Int32ub, # big endian - "play_end_player_trace_reward_data_list_size" / Int32ub, # big endian - - # ex_bonus_data_list - "ex_bonus_table_id" / Int32ub, - "achievement_status" / Int8ul, # result is either 0 or 1 - - # play_end_player_trace_reward_data_list - "common_reward_data_size" / Int32ub, - - # common_reward_data - "common_reward_type" / Int16ub, # short - "common_reward_id" / Int32ub, - "common_reward_num" / Int32ub, - - "multi_play_end_response_data_size" / Int32ub, # big endian - - # multi_play_end_response_data - "dummy_1" / Int8ul, # result is either 0 or 1 - "dummy_2" / Int8ul, # result is either 0 or 1 - "dummy_3" / Int8ul, # result is either 0 or 1 - ) - - resp_data = resp_struct.build(dict( - result=self.result, - play_end_response_data_size=self.play_end_response_data_size, - - rarity_up_occurrence_flag=self.rarity_up_occurrence_flag, - adventure_ex_area_occurrences_flag=self.adventure_ex_area_occurrences_flag, - ex_bonus_data_list_size=self.ex_bonus_data_list_size, - play_end_player_trace_reward_data_list_size=self.play_end_player_trace_reward_data_list_size, - - ex_bonus_table_id=self.ex_bonus_table_id, - achievement_status=self.achievement_status, - - common_reward_data_size=self.common_reward_data_size, - - common_reward_type=self.common_reward_type, - common_reward_id=self.common_reward_id, - common_reward_num=self.common_reward_num, - - multi_play_end_response_data_size=self.multi_play_end_response_data_size, - - dummy_1=self.dummy_1, - dummy_2=self.dummy_2, - dummy_3=self.dummy_3, - )) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.play_end_response_data) \ + + encode_arr_cls(self.multi_play_end_response_data) class SaoTrialTowerPlayStartRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: @@ -1714,160 +1018,56 @@ class SaoTrialTowerPlayStartRequest(SaoBaseRequest): off += tmp.get_size() self.multi_play_start_request_data.append(tmp) +class SaoTrialTowerPlayStartResponse(SaoBaseResponse): + def __init__(self, sesh_id: int, nickname: str) -> None: + super().__init__(GameconnectCmd.TRIAL_TOWER_PLAY_START_RESPONSE) + self.result = 1 + self.play_start_response_data: List[QuestScenePlayStartResponseData] = [QuestScenePlayStartResponseData.from_args(sesh_id, nickname)] + self.multi_play_start_response_data: List[QuestSceneMultiPlayStartResponseData] = [QuestSceneMultiPlayStartResponseData.from_args()] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.play_start_response_data) \ + + encode_arr_cls(self.multi_play_start_response_data) + class SaoTrialTowerPlayEndRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) off = 0 - ticket_id = decode_str(data, off) - self.ticket_id = ticket_id[0] - off += ticket_id[1] + self.ticket_id, new_off = decode_str(data, off) + off += new_off - user_id = decode_str(data, off) - self.user_id = user_id[0] - off += user_id[1] + self.user_id, new_off = decode_str(data, off) + off += new_off self.trial_tower_id = decode_int(data, off) off += INT_OFF - self.play_end_request_data_count = decode_int(data, off) - off += INT_OFF + self.play_end_request_data: List[QuestScenePlayEndRequestData] = [] + self.play_end_request_data, new_off = decode_arr_cls(data, off, QuestScenePlayEndRequestData) + off += new_off - self.play_end_request_data_list: List[PlayEndRequestData] = [] - for _ in range(self.play_end_request_data_count): - tmp = PlayEndRequestData(data, off) - off += tmp.get_size() - self.play_end_request_data_list.append(tmp) - - self.multi_play_end_request_data_count = decode_int(data, off) - off += INT_OFF - - self.multi_play_end_request_data_list: List[MultiPlayEndRequestData] = [] - for _ in range(self.multi_play_end_request_data_count): - tmp = MultiPlayEndRequestData(data, off) - off += tmp.get_size() - self.multi_play_end_request_data_list.append(tmp) + self.multi_play_end_request_data: List[QuestSceneMultiPlayEndRequestData] + self.multi_play_end_request_data, new_off = decode_arr_cls(data, off, QuestSceneMultiPlayEndRequestData) + off += new_off class SaoTrialTowerPlayEndResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.TRIAL_TOWER_PLAY_END_RESPONSE) self.result = 1 - self.play_end_response_data_size = 1 # Number of arrays - self.multi_play_end_response_data_size = 1 # Unused on solo play - self.trial_tower_play_end_updated_notification_data_size = 1 # Number of arrays - self.treasure_hunt_play_end_response_data_size = 1 # Number of arrays + self.play_end_response_data: List[QuestScenePlayEndResponseData] = [QuestScenePlayEndResponseData.from_args()] + self.multi_play_end_response_data: List[QuestSceneMultiPlayEndResponseData] = [QuestSceneMultiPlayEndResponseData.from_args()] + self.trial_tower_play_end_updated_notification_data: List[QuestTrialTowerPlayEndUpdatedNotificationData] = [QuestTrialTowerPlayEndUpdatedNotificationData.from_args()] + self.treasure_hunt_play_end_response_data: List[QuestTreasureHuntPlayEndResponseData] = [QuestTreasureHuntPlayEndResponseData.from_args()] - self.dummy_1 = 0 - self.dummy_2 = 0 - self.dummy_3 = 0 - - self.rarity_up_occurrence_flag = 0 - self.adventure_ex_area_occurrences_flag = 0 - self.ex_bonus_data_list_size = 1 # Number of arrays - self.play_end_player_trace_reward_data_list_size = 0 # Number of arrays - - self.ex_bonus_table_id = 0 # ExBonusTable.csv values, dont care for now - self.achievement_status = 1 - - self.common_reward_data_size = 1 # Number of arrays - - self.common_reward_type = 0 # dummy values from 2,101000000,1 from RewardTable.csv - self.common_reward_id = 0 - self.common_reward_num = 0 - - self.store_best_score_clear_time_flag = 0 - self.store_best_score_combo_num_flag = 0 - self.store_best_score_total_damage_flag = 0 - self.store_best_score_concurrent_destroying_num_flag = 0 - self.store_reaching_trial_tower_rank = 0 - - self.get_event_point = 0 - self.total_event_point = 0 - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "play_end_response_data_size" / Int32ub, # big endian - - "rarity_up_occurrence_flag" / Int8ul, # result is either 0 or 1 - "adventure_ex_area_occurrences_flag" / Int8ul, # result is either 0 or 1 - "ex_bonus_data_list_size" / Int32ub, # big endian - "play_end_player_trace_reward_data_list_size" / Int32ub, # big endian - - # ex_bonus_data_list - "ex_bonus_table_id" / Int32ub, - "achievement_status" / Int8ul, # result is either 0 or 1 - - # play_end_player_trace_reward_data_list - "common_reward_data_size" / Int32ub, - - # common_reward_data - "common_reward_type" / Int16ub, # short - "common_reward_id" / Int32ub, - "common_reward_num" / Int32ub, - - "multi_play_end_response_data_size" / Int32ub, # big endian - - # multi_play_end_response_data - "dummy_1" / Int8ul, # result is either 0 or 1 - "dummy_2" / Int8ul, # result is either 0 or 1 - "dummy_3" / Int8ul, # result is either 0 or 1 - - "trial_tower_play_end_updated_notification_data_size" / Int32ub, # big endian - - #trial_tower_play_end_updated_notification_data - "store_best_score_clear_time_flag" / Int8ul, # result is either 0 or 1 - "store_best_score_combo_num_flag" / Int8ul, # result is either 0 or 1 - "store_best_score_total_damage_flag" / Int8ul, # result is either 0 or 1 - "store_best_score_concurrent_destroying_num_flag" / Int8ul, # result is either 0 or 1 - "store_reaching_trial_tower_rank" / Int32ub, - - "treasure_hunt_play_end_response_data_size" / Int32ub, # big endian - - #treasure_hunt_play_end_response_data - "get_event_point" / Int32ub, - "total_event_point" / Int32ub, - ) - - resp_data = resp_struct.build(dict( - result=self.result, - play_end_response_data_size=self.play_end_response_data_size, - - rarity_up_occurrence_flag=self.rarity_up_occurrence_flag, - adventure_ex_area_occurrences_flag=self.adventure_ex_area_occurrences_flag, - ex_bonus_data_list_size=self.ex_bonus_data_list_size, - play_end_player_trace_reward_data_list_size=self.play_end_player_trace_reward_data_list_size, - - ex_bonus_table_id=self.ex_bonus_table_id, - achievement_status=self.achievement_status, - - common_reward_data_size=self.common_reward_data_size, - - common_reward_type=self.common_reward_type, - common_reward_id=self.common_reward_id, - common_reward_num=self.common_reward_num, - - multi_play_end_response_data_size=self.multi_play_end_response_data_size, - - dummy_1=self.dummy_1, - dummy_2=self.dummy_2, - dummy_3=self.dummy_3, - - trial_tower_play_end_updated_notification_data_size=self.trial_tower_play_end_updated_notification_data_size, - store_best_score_clear_time_flag=self.store_best_score_clear_time_flag, - store_best_score_combo_num_flag=self.store_best_score_combo_num_flag, - store_best_score_total_damage_flag=self.store_best_score_total_damage_flag, - store_best_score_concurrent_destroying_num_flag=self.store_best_score_concurrent_destroying_num_flag, - store_reaching_trial_tower_rank=self.store_reaching_trial_tower_rank, - - treasure_hunt_play_end_response_data_size=self.treasure_hunt_play_end_response_data_size, - - get_event_point=self.get_event_point, - total_event_point=self.total_event_point, - )) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.play_end_response_data) \ + + encode_arr_cls(self.multi_play_end_response_data) \ + + encode_arr_cls(self.trial_tower_play_end_updated_notification_data) \ + + encode_arr_cls(self.treasure_hunt_play_end_response_data) class SaoEpisodePlayEndUnanalyzedLogFixedRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: @@ -1888,1150 +1088,597 @@ class SaoEpisodePlayEndUnanalyzedLogFixedRequest(SaoBaseRequest): off += BYTE_OFF class SaoEpisodePlayEndUnanalyzedLogFixedResponse(SaoBaseResponse): - def __init__(self, cmd, end_session_data) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.EPISODE_PLAY_END_UNANALYZED_LOG_FIXED_RESPONSE) self.result = 1 + self.play_end_unanalyzed_log_reward_data_list: List[QuestScenePlayEndUnanalyzedLogRewardData] = [] - self.unanalyzed_log_grade_id = [] - - self.common_reward_type = [] - self.common_reward_id = [] - self.common_reward_num = 1 - - for x in range(len(end_session_data)): - self.common_reward_id.append(end_session_data[x]) - - with open('titles/sao/data/RewardTable.csv', 'r') as f: - keys_unanalyzed = next(f).strip().split(',') - data_unanalyzed = list(DictReader(f, fieldnames=keys_unanalyzed)) - - for i in range(len(data_unanalyzed)): - if int(data_unanalyzed[i]["CommonRewardId"]) == int(end_session_data[x]): - self.unanalyzed_log_grade_id.append(int(data_unanalyzed[i]["UnanalyzedLogGradeId"])) - self.common_reward_type.append(int(data_unanalyzed[i]["CommonRewardType"])) - break - - self.unanalyzed_log_grade_id = list(map(int,self.unanalyzed_log_grade_id)) #int - self.common_reward_type = list(map(int,self.common_reward_type)) #int - self.common_reward_id = list(map(int,self.common_reward_id)) #int - def make(self) -> bytes: - #new stuff - common_reward_data_struct = Struct( - "common_reward_type" / Int16ub, - "common_reward_id" / Int32ub, - "common_reward_num" / Int32ub, - ) - - play_end_unanalyzed_log_reward_data_list_struct = Struct( - "unanalyzed_log_grade_id" / Int32ub, - "common_reward_data_size" / Rebuild(Int32ub, len_(this.common_reward_data)), # big endian - "common_reward_data" / Array(this.common_reward_data_size, common_reward_data_struct), - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "play_end_unanalyzed_log_reward_data_list_size" / Rebuild(Int32ub, len_(this.play_end_unanalyzed_log_reward_data_list)), # big endian - "play_end_unanalyzed_log_reward_data_list" / Array(this.play_end_unanalyzed_log_reward_data_list_size, play_end_unanalyzed_log_reward_data_list_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - play_end_unanalyzed_log_reward_data_list_size=0, - play_end_unanalyzed_log_reward_data_list=[], - ))) - - for i in range(len(self.common_reward_id)): - reward_resp_data = dict( - unanalyzed_log_grade_id=self.unanalyzed_log_grade_id[i], - common_reward_data_size=0, - common_reward_data=[], - ) - - reward_resp_data["common_reward_data"].append(dict( - common_reward_type=self.common_reward_type[i], - common_reward_id=self.common_reward_id[i], - common_reward_num=self.common_reward_num, - )) - - resp_data.play_end_unanalyzed_log_reward_data_list.append(reward_resp_data) - - resp_data["play_end_unanalyzed_log_reward_data_list_size"] = len(resp_data.play_end_unanalyzed_log_reward_data_list) - for i in range(len(resp_data.play_end_unanalyzed_log_reward_data_list)): - resp_data.play_end_unanalyzed_log_reward_data_list[i]["common_reward_data_size"] = len(resp_data.play_end_unanalyzed_log_reward_data_list[i]["common_reward_data"]) - - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data - -class SaoGetQuestSceneUserDataListRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.user_id = decode_str(data, 0)[0] + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.play_end_unanalyzed_log_reward_data_list) class SaoGetQuestSceneUserDataListResponse(SaoBaseResponse): - def __init__(self, cmd, quest_data) -> None: - super().__init__(cmd) - self.length = None + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_QUEST_SCENE_USER_DATA_LIST_RESPONSE) self.result = 1 + self.quest_scene_user_data_list: List[QuestSceneUserData] = [] - # quest_scene_user_data_list_size - self.quest_type = [] - self.quest_scene_id = [] - self.clear_flag = [] - - # quest_scene_best_score_user_data - self.clear_time = [] - self.combo_num = [] - self.total_damage = [] #string - self.concurrent_destroying_num = [] - - for i in range(len(quest_data)): - self.quest_type.append(1) - self.quest_scene_id.append(quest_data[i][2]) - self.clear_flag.append(int(quest_data[i][3])) - - self.clear_time.append(quest_data[i][4]) - self.combo_num.append(quest_data[i][5]) - self.total_damage.append(0) #totally absurd but Int16ul[1] is a big problem due to different lenghts... - self.concurrent_destroying_num.append(quest_data[i][7]) - - # quest_scene_ex_bonus_user_data_list - self.achievement_flag = [1,1,1] - self.ex_bonus_table_id = [1,2,3] - - - self.quest_type = list(map(int,self.quest_type)) #int - self.quest_scene_id = list(map(int,self.quest_scene_id)) #int - self.clear_flag = list(map(int,self.clear_flag)) #int - self.clear_time = list(map(int,self.clear_time)) #int - self.combo_num = list(map(int,self.combo_num)) #int - self.total_damage = list(map(str,self.total_damage)) #string - self.concurrent_destroying_num = list(map(int,self.combo_num)) #int - def make(self) -> bytes: - #new stuff - quest_scene_ex_bonus_user_data_list_struct = Struct( - "ex_bonus_table_id" / Int32ub, # big endian - "achievement_flag" / Int8ul, # result is either 0 or 1 - ) - - quest_scene_best_score_user_data_struct = Struct( - "clear_time" / Int32ub, # big endian - "combo_num" / Int32ub, # big endian - "total_damage_size" / Int32ub, # big endian - "total_damage" / Int16ul[1], - "concurrent_destroying_num" / Int16ub, - ) - - quest_scene_user_data_list_struct = Struct( - "quest_type" / Int8ul, # result is either 0 or 1 - "quest_scene_id" / Int16ub, #short - "clear_flag" / Int8ul, # result is either 0 or 1 - "quest_scene_best_score_user_data_size" / Rebuild(Int32ub, len_(this.quest_scene_best_score_user_data)), # big endian - "quest_scene_best_score_user_data" / Array(this.quest_scene_best_score_user_data_size, quest_scene_best_score_user_data_struct), - "quest_scene_ex_bonus_user_data_list_size" / Rebuild(Int32ub, len_(this.quest_scene_ex_bonus_user_data_list)), # big endian - "quest_scene_ex_bonus_user_data_list" / Array(this.quest_scene_ex_bonus_user_data_list_size, quest_scene_ex_bonus_user_data_list_struct), - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "quest_scene_user_data_list_size" / Rebuild(Int32ub, len_(this.quest_scene_user_data_list)), # big endian - "quest_scene_user_data_list" / Array(this.quest_scene_user_data_list_size, quest_scene_user_data_list_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - quest_scene_user_data_list_size=0, - quest_scene_user_data_list=[], - ))) - - for i in range(len(self.quest_scene_id)): - quest_resp_data = dict( - quest_type=self.quest_type[i], - quest_scene_id=self.quest_scene_id[i], - clear_flag=self.clear_flag[i], - - quest_scene_best_score_user_data_size=0, - quest_scene_best_score_user_data=[], - quest_scene_ex_bonus_user_data_list_size=0, - quest_scene_ex_bonus_user_data_list=[], - ) - - quest_resp_data["quest_scene_best_score_user_data"].append(dict( - clear_time=self.clear_time[i], - combo_num=self.combo_num[i], - total_damage_size=len(self.total_damage[i]) * 2, - total_damage=[ord(x) for x in self.total_damage[i]], - concurrent_destroying_num=self.concurrent_destroying_num[i], - )) - - resp_data.quest_scene_user_data_list.append(quest_resp_data) - - resp_data["quest_scene_user_data_list_size"] = len(resp_data.quest_scene_user_data_list) - for i in range(len(resp_data.quest_scene_user_data_list)): - resp_data.quest_scene_user_data_list[i]["quest_scene_best_score_user_data_size"] = len(resp_data.quest_scene_user_data_list[i]["quest_scene_best_score_user_data"]) - - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.quest_scene_user_data_list) class SaoCheckYuiMedalGetConditionRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + self.user_id, new_off = decode_str(data, off) + off += new_off class SaoCheckYuiMedalGetConditionResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self, elapsed_days: int = 0, get_num: int = 0) -> None: + super().__init__(GameconnectCmd.CHECK_YUI_MEDAL_GET_CONDITION_RESPONSE) self.result = 1 - self.get_flag = 1 - self.elapsed_days = 0 - self.get_yui_medal_num = 0 + self.get_flag = int(get_num > 0) + self.elapsed_days = elapsed_days + self.get_yui_medal_num = get_num def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "get_flag" / Int8ul, # result is either 0 or 1 - "elapsed_days" / Int16ub, #short - "get_yui_medal_num" / Int16ub, #short - ) - - resp_data = resp_struct.build(dict( - result=self.result, - get_flag=self.get_flag, - elapsed_days=self.elapsed_days, - get_yui_medal_num=self.get_yui_medal_num, - )) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_byte(self.get_flag) \ + + encode_short(self.elapsed_days) \ + + encode_short(self.get_yui_medal_num) \ class SaoGetYuiMedalBonusUserDataRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.yui_medal_bonus_id = decode_int(data, off) + off += INT_OFF class SaoGetYuiMedalBonusUserDataResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self, elapsed_days: int = 0, loop_num: int = 0) -> None: + super().__init__(GameconnectCmd.GET_YUI_MEDAL_BONUS_USER_DATA_RESPONSE) self.result = 1 - self.data_size = 1 # number of arrays - - self.elapsed_days = 1 - self.loop_num = 1 - self.last_check_date = "20230520193000" - self.last_get_date = "20230520193000" + self.data: List[YuiMedalBonusUserData] = [YuiMedalBonusUserData.from_args(elapsed_days, loop_num)] def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "data_size" / Int32ub, # big endian - - "elapsed_days" / Int32ub, # big endian - "loop_num" / Int32ub, # big endian - "last_check_date_size" / Int32ub, # big endian - "last_check_date" / Int16ul[len(self.last_check_date)], - "last_get_date_size" / Int32ub, # big endian - "last_get_date" / Int16ul[len(self.last_get_date)], - ) - - resp_data = resp_struct.build(dict( - result=self.result, - data_size=self.data_size, - - elapsed_days=self.elapsed_days, - loop_num=self.loop_num, - last_check_date_size=len(self.last_check_date) * 2, - last_check_date=[ord(x) for x in self.last_check_date], - last_get_date_size=len(self.last_get_date) * 2, - last_get_date=[ord(x) for x in self.last_get_date], - - )) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data) class SaoCheckProfileCardUsedRewardRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + self.user_id, new_off = decode_str(data, off) + off += new_off class SaoCheckProfileCardUsedRewardResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.CHECK_PROFILE_CARD_USED_REWARD_RESPONSE) self.result = 1 - self.get_flag = 1 + self.get_flag = 0 self.used_num = 0 - self.get_vp = 1 + self.get_vp = 0 def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "get_flag" / Int8ul, # result is either 0 or 1 - "used_num" / Int32ub, # big endian - "get_vp" / Int32ub, # big endian - ) + return super().make() \ + + encode_byte(self.result) \ + + encode_byte(self.get_flag) \ + + encode_int(self.used_num) \ + + encode_int(self.get_vp) - resp_data = resp_struct.build(dict( - result=self.result, - get_flag=self.get_flag, - used_num=self.used_num, - get_vp=self.get_vp, - - )) +class SaoDisposalResourceRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off - self.length = len(resp_data) - return super().make() + resp_data + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.disposal_common_reward_user_data_list: List[CommonRewardUserData] = [] + self.disposal_common_reward_user_data_list, new_off = decode_arr_cls(data, off, CommonRewardUserData) + off += new_off + +class SaoDisposalResourceResponse(SaoBaseResponse): + def __init__(self, get_col: int = 0) -> None: + super().__init__(GameconnectCmd.DISPOSAL_RESOURCE_RESPONSE) + self.result = 1 + self.get_col = get_col + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_int(self.get_col) class SaoSynthesizeEnhancementHeroLogRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) off = 0 - ticket_id = decode_str(data, off) - self.ticket_id = ticket_id[0] - off += ticket_id[1] + self.ticket_id, new_off = decode_str(data, off) + off += new_off - user_id = decode_str(data, off) - self.user_id = user_id[0] - off += user_id[1] + self.user_id, new_off = decode_str(data, off) + off += new_off - origin_user_hero_log_id = decode_str(data, off) - self.origin_user_hero_log_id = origin_user_hero_log_id[0] - off += origin_user_hero_log_id[1] + self.origin_user_hero_log_id, new_off = decode_str(data, off) + off += new_off - self.material_common_reward_user_data_list: List[MaterialCommonRewardUserData] = [] - - self.material_common_reward_user_data_count = decode_int(data, off) - off += INT_OFF - - for _ in range(self.material_common_reward_user_data_count): - mat = MaterialCommonRewardUserData(data, off) - off += mat.get_size() - self.material_common_reward_user_data_list.append(mat) + self.material_common_reward_user_data_list: List[CommonRewardUserData] = [] + self.material_common_reward_user_data_list, new_off = decode_arr_cls(data, off, CommonRewardUserData) + off += new_off class SaoSynthesizeEnhancementHeroLogResponse(SaoBaseResponse): - def __init__(self, cmd, hero_data) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.SYNTHESIZE_ENHANCEMENT_HERO_LOG_RESPONSE) self.result = 1 + self.after_hero_log_user_data: List[HeroLogUserData] = [] - # Calculate level based off experience and the CSV list - with open(r'titles/sao/data/HeroLogLevel.csv') as csv_file: - csv_reader = csv.reader(csv_file, delimiter=',') - line_count = 0 - data = [] - rowf = False - for row in csv_reader: - if rowf==False: - rowf=True - else: - data.append(row) - - exp = hero_data[4] - - for e in range(0,len(data)): - if exp>=int(data[e][1]) and exp bytes: - #new stuff - - hero_log_user_data_list_struct = Struct( - "user_hero_log_id_size" / Int32ub, # big endian - "user_hero_log_id" / Int16ul[9], #string - "hero_log_id" / Int32ub, #int - "log_level" / Int16ub, #short - "max_log_level_extended_num" / Int16ub, #short - "log_exp" / Int32ub, #int - "possible_awakening_flag" / Int8ul, # result is either 0 or 1 - "awakening_stage" / Int16ub, #short - "awakening_exp" / Int32ub, #int - "skill_slot_correction_value" / Int8ul, # result is either 0 or 1 - "last_set_skill_slot1_skill_id" / Int16ub, #short - "last_set_skill_slot2_skill_id" / Int16ub, #short - "last_set_skill_slot3_skill_id" / Int16ub, #short - "last_set_skill_slot4_skill_id" / Int16ub, #short - "last_set_skill_slot5_skill_id" / Int16ub, #short - "property1_property_id" / Int32ub, - "property1_value1" / Int32ub, - "property1_value2" / Int32ub, - "property2_property_id" / Int32ub, - "property2_value1" / Int32ub, - "property2_value2" / Int32ub, - "property3_property_id" / Int32ub, - "property3_value1" / Int32ub, - "property3_value2" / Int32ub, - "property4_property_id" / Int32ub, - "property4_value1" / Int32ub, - "property4_value2" / Int32ub, - "converted_card_num" / Int16ub, - "shop_purchase_flag" / Int8ul, # result is either 0 or 1 - "protect_flag" / Int8ul, # result is either 0 or 1 - "get_date_size" / Int32ub, # big endian - "get_date" / Int16ul[len(self.get_date)], - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "hero_log_user_data_list_size" / Rebuild(Int32ub, len_(this.hero_log_user_data_list)), # big endian - "hero_log_user_data_list" / Array(this.hero_log_user_data_list_size, hero_log_user_data_list_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - hero_log_user_data_list_size=0, - hero_log_user_data_list=[], - ))) - - hero_data = dict( - user_hero_log_id_size=len(self.user_hero_log_id) * 2, - user_hero_log_id=[ord(x) for x in self.user_hero_log_id], - hero_log_id=self.hero_log_id, - log_level=self.log_level, - max_log_level_extended_num=self.max_log_level_extended_num, - log_exp=self.log_exp, - possible_awakening_flag=self.possible_awakening_flag, - awakening_stage=self.awakening_stage, - awakening_exp=self.awakening_exp, - skill_slot_correction_value=self.skill_slot_correction_value, - last_set_skill_slot1_skill_id=self.last_set_skill_slot1_skill_id, - last_set_skill_slot2_skill_id=self.last_set_skill_slot2_skill_id, - last_set_skill_slot3_skill_id=self.last_set_skill_slot3_skill_id, - last_set_skill_slot4_skill_id=self.last_set_skill_slot4_skill_id, - last_set_skill_slot5_skill_id=self.last_set_skill_slot5_skill_id, - property1_property_id=self.property1_property_id, - property1_value1=self.property1_value1, - property1_value2=self.property1_value2, - property2_property_id=self.property2_property_id, - property2_value1=self.property2_value1, - property2_value2=self.property2_value2, - property3_property_id=self.property3_property_id, - property3_value1=self.property3_value1, - property3_value2=self.property3_value2, - property4_property_id=self.property4_property_id, - property4_value1=self.property4_value1, - property4_value2=self.property4_value2, - converted_card_num=self.converted_card_num, - shop_purchase_flag=self.shop_purchase_flag, - protect_flag=self.protect_flag, - get_date_size=len(self.get_date) * 2, - get_date=[ord(x) for x in self.get_date], - - ) - - resp_data.hero_log_user_data_list.append(hero_data) - - resp_data["hero_log_user_data_list_size"] = len(resp_data.hero_log_user_data_list) - - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.after_hero_log_user_data) class SaoSynthesizeEnhancementEquipmentRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) off = 0 - ticket_id = decode_str(data, off) - self.ticket_id = ticket_id[0] - off += ticket_id[1] + self.ticket_id, new_off = decode_str(data, off) + off += new_off - user_id = decode_str(data, off) - self.user_id = user_id[0] - off += user_id[1] - - origin_user_equipment_id = decode_str(data, off) - self.origin_user_equipment_id = origin_user_equipment_id[0] - off += origin_user_equipment_id[1] - - self.material_common_reward_user_data_list: List[MaterialCommonRewardUserData] = [] - - self.material_common_reward_user_data_count = decode_int(data, off) - off += INT_OFF + self.user_id, new_off = decode_str(data, off) + off += new_off - for _ in range(self.material_common_reward_user_data_count): - mat = MaterialCommonRewardUserData(data, off) - off += mat.get_size() - self.material_common_reward_user_data_list.append(mat) + self.origin_user_equipment_id, new_off = decode_str(data, off) + off += new_off + + self.material_common_reward_user_data_list: List[CommonRewardUserData] = [] + self.material_common_reward_user_data_list, new_off = decode_arr_cls(data, off, CommonRewardUserData) + off += new_off class SaoSynthesizeEnhancementEquipmentResponse(SaoBaseResponse): - def __init__(self, cmd, synthesize_equipment_data) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.SYNTHESIZE_ENHANCEMENT_EQUIPMENT_RESPONSE) self.result = 1 - equipment_level = 0 + self.after_equipment_user_data: List[EquipmentUserData] = [] - # Calculate level based off experience and the CSV list - with open(r'titles/sao/data/EquipmentLevel.csv') as csv_file: - csv_reader = csv.reader(csv_file, delimiter=',') - line_count = 0 - data = [] - rowf = False - for row in csv_reader: - if rowf==False: - rowf=True - else: - data.append(row) - - exp = synthesize_equipment_data[4] - - for e in range(0,len(data)): - if exp>=int(data[e][1]) and exp bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.after_equipment_user_data) - after_equipment_user_data_struct = Struct( - "user_equipment_id_size" / Int32ub, # big endian - "user_equipment_id" / Int16ul[9], #string - "equipment_id" / Int32ub, #int - "enhancement_value" / Int16ub, #short - "max_enhancement_value_extended_num" / Int16ub, #short - "enhancement_exp" / Int32ub, #int - "possible_awakening_flag" / Int8ul, # result is either 0 or 1 - "awakening_stage" / Int16ub, #short - "awakening_exp" / Int32ub, #int - "property1_property_id" / Int32ub, - "property1_value1" / Int32ub, - "property1_value2" / Int32ub, - "property2_property_id" / Int32ub, - "property2_value1" / Int32ub, - "property2_value2" / Int32ub, - "property3_property_id" / Int32ub, - "property3_value1" / Int32ub, - "property3_value2" / Int32ub, - "property4_property_id" / Int32ub, - "property4_value1" / Int32ub, - "property4_value2" / Int32ub, - "converted_card_num" / Int16ub, - "shop_purchase_flag" / Int8ul, # result is either 0 or 1 - "protect_flag" / Int8ul, # result is either 0 or 1 - "get_date_size" / Int32ub, # big endian - "get_date" / Int16ul[len(self.get_date)], - ) +class SaoGetAdventureExecUserDataResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_ADVENTURE_EXEC_USER_DATA_RESPONSE) + self.result = 1 + self.adventure_exec_user_data: List[AdventureExecUserData] = [] - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "after_equipment_user_data_size" / Rebuild(Int32ub, len_(this.after_equipment_user_data)), # big endian - "after_equipment_user_data" / Array(this.after_equipment_user_data_size, after_equipment_user_data_struct), - ) + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.adventure_exec_user_data) - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - after_equipment_user_data_size=0, - after_equipment_user_data=[], - ))) +class SaoGetChatSideStoryUserDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_CHAT_SIDE_STORY_USER_DATA_LIST_RESPONSE) + self.result = 1 + self.chat_side_story_user_data_list: List[ChatSideStoryUserData] = [] - synthesize_equipment_data = dict( - user_equipment_id_size=len(self.user_equipment_id) * 2, - user_equipment_id=[ord(x) for x in self.user_equipment_id], - equipment_id=self.equipment_id, - enhancement_value=self.enhancement_value, - max_enhancement_value_extended_num=self.max_enhancement_value_extended_num, - enhancement_exp=self.enhancement_exp, - possible_awakening_flag=self.possible_awakening_flag, - awakening_stage=self.awakening_stage, - awakening_exp=self.awakening_exp, - property1_property_id=self.property1_property_id, - property1_value1=self.property1_value1, - property1_value2=self.property1_value2, - property2_property_id=self.property2_property_id, - property2_value1=self.property2_value1, - property2_value2=self.property2_value2, - property3_property_id=self.property3_property_id, - property3_value1=self.property3_value1, - property3_value2=self.property3_value2, - property4_property_id=self.property4_property_id, - property4_value1=self.property4_value1, - property4_value2=self.property4_value2, - converted_card_num=self.converted_card_num, - shop_purchase_flag=self.shop_purchase_flag, - protect_flag=self.protect_flag, - get_date_size=len(self.get_date) * 2, - get_date=[ord(x) for x in self.get_date], + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.chat_side_story_user_data_list) - ) - - resp_data.after_equipment_user_data.append(synthesize_equipment_data) +class SaoBeginnerMissionAdConfirmNotificationRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off - resp_data["after_equipment_user_data_size"] = len(resp_data.after_equipment_user_data) + self.user_id, new_off = decode_str(data, off) + off += new_off - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF class SaoGetDefragMatchBasicDataRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.defrag_match_id = decode_int(data, off) + off += INT_OFF class SaoGetDefragMatchBasicDataResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_DEFRAG_MATCH_BASIC_DATA_RESPONSE) self.result = 1 - self.defrag_match_basic_user_data_size = 1 # number of arrays - - self.seed_flag = 1 - self.ad_confirm_flag = 1 - self.total_league_point = 0 - self.have_league_score = 0 - self.class_num = 1 # 1 to 6 - self.hall_of_fame_confirm_flag = 0 + self.defrag_match_basic_user_data: List[DefragMatchBasicUserData] = [] def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "defrag_match_basic_user_data_size" / Int32ub, # big endian - - "seed_flag" / Int16ub, #short - "ad_confirm_flag" / Int8ul, # result is either 0 or 1 - "total_league_point" / Int32ub, #int - "have_league_score" / Int16ub, #short - "class_num" / Int16ub, #short - "hall_of_fame_confirm_flag" / Int8ul, # result is either 0 or 1 - - ) - - resp_data = resp_struct.build(dict( - result=self.result, - defrag_match_basic_user_data_size=self.defrag_match_basic_user_data_size, - - seed_flag=self.seed_flag, - ad_confirm_flag=self.ad_confirm_flag, - total_league_point=self.total_league_point, - have_league_score=self.have_league_score, - class_num=self.class_num, - hall_of_fame_confirm_flag=self.hall_of_fame_confirm_flag, - )) - - self.length = len(resp_data) - return super().make() + resp_data + ret = encode_byte(self.result) + ret += encode_arr_cls(self.defrag_match_basic_user_data) + return super().make() + ret class SaoGetDefragMatchRankingUserDataRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.defrag_match_id = decode_int(data, off) + off += INT_OFF class SaoGetDefragMatchRankingUserDataResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self, profile: Dict) -> None: + super().__init__(GameconnectCmd.GET_DEFRAG_MATCH_RANKING_USER_DATA_RESPONSE) self.result = 1 - self.ranking_user_data_size = 1 # number of arrays + self.ranking_user_data: List[DefragMatchRankingUserData] = [DefragMatchRankingUserData.from_args(profile)] - self.league_point_rank = 1 - self.league_score_rank = 1 - self.nick_name = "PLAYER" - self.setting_title_id = 20005 # Default saved during profile creation, no changing for those atm - self.favorite_hero_log_id = 101000010 # Default saved during profile creation - self.favorite_hero_log_awakening_stage = 0 - self.favorite_support_log_id = 0 - self.favorite_support_log_awakening_stage = 0 - self.total_league_point = 1 - self.have_league_score = 1 - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "ranking_user_data_size" / Int32ub, # big endian - - "league_point_rank" / Int32ub, #int - "league_score_rank" / Int32ub, #int - "nick_name_size" / Int32ub, # big endian - "nick_name" / Int16ul[len(self.nick_name)], - "setting_title_id" / Int32ub, #int - "favorite_hero_log_id" / Int32ub, #int - "favorite_hero_log_awakening_stage" / Int16ub, #short - "favorite_support_log_id" / Int32ub, #int - "favorite_support_log_awakening_stage" / Int16ub, #short - "total_league_point" / Int32ub, #int - "have_league_score" / Int16ub, #short - ) - - resp_data = resp_struct.build(dict( - result=self.result, - ranking_user_data_size=self.ranking_user_data_size, - - league_point_rank=self.league_point_rank, - league_score_rank=self.league_score_rank, - nick_name_size=len(self.nick_name) * 2, - nick_name=[ord(x) for x in self.nick_name], - setting_title_id=self.setting_title_id, - favorite_hero_log_id=self.favorite_hero_log_id, - favorite_hero_log_awakening_stage=self.favorite_hero_log_awakening_stage, - favorite_support_log_id=self.favorite_support_log_id, - favorite_support_log_awakening_stage=self.favorite_support_log_awakening_stage, - total_league_point=self.total_league_point, - have_league_score=self.have_league_score, - )) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.ranking_user_data) class SaoGetDefragMatchLeaguePointRankingListRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.get_rank_start_num = decode_short(data, off) + off += SHORT_OFF + + self.get_rank_end_num = decode_short(data, off) + off += SHORT_OFF class SaoGetDefragMatchLeaguePointRankingListResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_DEFRAG_MATCH_LEAGUE_POINT_RANKING_LIST_RESPONSE) self.result = 1 - self.ranking_user_data_size = 1 # number of arrays + self.ranking_data_list: List[DefragMatchLeaguePointRankingData] = [] - self.rank = 1 - self.user_id = "1" - self.store_id = "123" - self.store_name = "ARTEMiS" - self.nick_name = "PLAYER" - self.setting_title_id = 20005 - self.favorite_hero_log_id = 101000010 - self.favorite_hero_log_awakening_stage = 0 - self.favorite_support_log_id = 0 - self.favorite_support_log_awakening_stage = 0 - self.class_num = 1 - self.total_league_point = 1 - def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "ranking_user_data_size" / Int32ub, # big endian - - "rank" / Int32ub, #int - "user_id_size" / Int32ub, # big endian - "user_id" / Int16ul[len(self.user_id)], - "store_id_size" / Int32ub, # big endian - "store_id" / Int16ul[len(self.store_id)], - "store_name_size" / Int32ub, # big endian - "store_name" / Int16ul[len(self.store_name)], - "nick_name_size" / Int32ub, # big endian - "nick_name" / Int16ul[len(self.nick_name)], - "setting_title_id" / Int32ub, #int - "favorite_hero_log_id" / Int32ub, #int - "favorite_hero_log_awakening_stage" / Int16ub, #short - "favorite_support_log_id" / Int32ub, #int - "favorite_support_log_awakening_stage" / Int16ub, #short - "class_num" / Int16ub, #short - "total_league_point" / Int32ub, #int - ) - - resp_data = resp_struct.build(dict( - result=self.result, - ranking_user_data_size=self.ranking_user_data_size, - - rank=self.rank, - user_id_size=len(self.user_id) * 2, - user_id=[ord(x) for x in self.user_id], - store_id_size=len(self.store_id) * 2, - store_id=[ord(x) for x in self.store_id], - store_name_size=len(self.store_name) * 2, - store_name=[ord(x) for x in self.store_name], - nick_name_size=len(self.nick_name) * 2, - nick_name=[ord(x) for x in self.nick_name], - setting_title_id=self.setting_title_id, - favorite_hero_log_id=self.favorite_hero_log_id, - favorite_hero_log_awakening_stage=self.favorite_hero_log_awakening_stage, - favorite_support_log_id=self.favorite_support_log_id, - favorite_support_log_awakening_stage=self.favorite_support_log_awakening_stage, - class_num=self.class_num, - total_league_point=self.total_league_point, - )) - - self.length = len(resp_data) - return super().make() + resp_data + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.ranking_data_list) class SaoGetDefragMatchLeagueScoreRankingListRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.get_rank_start_num = decode_short(data, off) + off += SHORT_OFF + + self.get_rank_end_num = decode_short(data, off) + off += SHORT_OFF class SaoGetDefragMatchLeagueScoreRankingListResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_DEFRAG_MATCH_LEAGUE_SCORE_RANKING_LIST_RESPONSE) self.result = 1 - self.ranking_user_data_size = 1 # number of arrays - - self.rank = 1 - self.user_id = "1" - self.store_id = "123" - self.store_name = "ARTEMiS" - self.nick_name = "PLAYER" - self.setting_title_id = 20005 - self.favorite_hero_log_id = 101000010 - self.favorite_hero_log_awakening_stage = 0 - self.favorite_support_log_id = 0 - self.favorite_support_log_awakening_stage = 0 - self.class_num = 1 - self.have_league_score = 1 + self.ranking_user_data: List[DefragMatchLeagueScoreRankingList] = [] def make(self) -> bytes: # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "ranking_user_data_size" / Int32ub, # big endian - - "rank" / Int32ub, #int - "user_id_size" / Int32ub, # big endian - "user_id" / Int16ul[len(self.user_id)], - "store_id_size" / Int32ub, # big endian - "store_id" / Int16ul[len(self.store_id)], - "store_name_size" / Int32ub, # big endian - "store_name" / Int16ul[len(self.store_name)], - "nick_name_size" / Int32ub, # big endian - "nick_name" / Int16ul[len(self.nick_name)], - "setting_title_id" / Int32ub, #int - "favorite_hero_log_id" / Int32ub, #int - "favorite_hero_log_awakening_stage" / Int16ub, #short - "favorite_support_log_id" / Int32ub, #int - "favorite_support_log_awakening_stage" / Int16ub, #short - "class_num" / Int16ub, #short - "have_league_score" / Int16ub, #short - ) - - resp_data = resp_struct.build(dict( - result=self.result, - ranking_user_data_size=self.ranking_user_data_size, - - rank=self.rank, - user_id_size=len(self.user_id) * 2, - user_id=[ord(x) for x in self.user_id], - store_id_size=len(self.store_id) * 2, - store_id=[ord(x) for x in self.store_id], - store_name_size=len(self.store_name) * 2, - store_name=[ord(x) for x in self.store_name], - nick_name_size=len(self.nick_name) * 2, - nick_name=[ord(x) for x in self.nick_name], - setting_title_id=self.setting_title_id, - favorite_hero_log_id=self.favorite_hero_log_id, - favorite_hero_log_awakening_stage=self.favorite_hero_log_awakening_stage, - favorite_support_log_id=self.favorite_support_log_id, - favorite_support_log_awakening_stage=self.favorite_support_log_awakening_stage, - class_num=self.class_num, - have_league_score=self.have_league_score, - )) - - self.length = len(resp_data) + resp_data = encode_byte(self.result) + resp_data += encode_arr_cls(self.ranking_user_data) return super().make() + resp_data +class SaoGetBeginnerMissionProgressesUserDataListRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + +class SaoGetBeginnerMissionProgressesUserDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_BEGINNER_MISSION_PROGRESSES_USER_DATA_LIST_RESPONSE) + self.result = 1 + self.data_list: List[BeginnerMissionProgressesUserData] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetBeginnerMissionSeatProgressesUserDataListRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + +class SaoGetBeginnerMissionSeatProgressesUserDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_BEGINNER_MISSION_SEAT_PROGRESSES_USER_DATA_LIST_RESPONSE) + self.result = 1 + self.data_list: List[BeginnerMissionSeatProgressesUserData] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetLinkedSiteRegCampaignUserDataRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.linked_site_reg_campaign_id = decode_int(data, off) + off += INT_OFF + +class SaoGetLinkedSiteRegCampaignUserDataResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_LINKED_SITE_REG_CAMPAIGN_USER_DATA_RESPONSE) + self.result = 1 + self.data: List[LinkedSiteRegCampaignUserData] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data) + +class SaoGetHeroLogUnitUserDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_HERO_LOG_UNIT_USER_DATA_LIST_RESPONSE) + self.result = 1 + self.hero_log_unit_user_data_list: List[HeroLogUnitUserData] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.hero_log_unit_user_data_list) + +class SaoGetCharaUnitUserDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_CHARA_UNIT_USER_DATA_LIST_RESPONSE) + self.result = 1 + self.chara_unit_user_data_list: List[CharaUnitUserData] = [] + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.chara_unit_user_data_list) + +class SaoBeginnerMissionAdConfirmNotificationRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + class SaoBnidSerialCodeCheckRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.bnid_serial_code, new_off = decode_str(data, off) + off += new_off class SaoBnidSerialCodeCheckResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) - + def __init__(self) -> None: + super().__init__(GameconnectCmd.BNID_SERIAL_CODE_CHECK_RESPONSE) self.result = 1 - self.bnid_item_id = "130050" + self.bnid_item_id = "" self.use_status = 0 - + def make(self) -> bytes: - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "bnid_item_id_size" / Int32ub, # big endian - "bnid_item_id" / Int16ul[len(self.bnid_item_id)], - "use_status" / Int8ul, # result is either 0 or 1 - ) + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.bnid_item_id) \ + + encode_byte(self.use_status) - resp_data = resp_struct.build(dict( - result=self.result, - bnid_item_id_size=len(self.bnid_item_id) * 2, - bnid_item_id=[ord(x) for x in self.bnid_item_id], - use_status=self.use_status, - )) +class SaoBnidSerialCodeEntryByAppendixCardRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off - self.length = len(resp_data) - return super().make() + resp_data + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.bnid_serial_code, new_off = decode_str(data, off) + off += new_off + +class SaoBnidSerialCodeEntryByAppendixCardResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.BNID_SERIAL_CODE_ENTRY_BY_APPENDIX_CARD_RESPONSE) + self.result = 1 + self.get_bnid_item_id = "" + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.get_bnid_item_id) + +class SaoDischargeProfileCardRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.hero_log_user_hero_log_id, new_off = decode_str(data, off) + off += new_off + + self.main_weapon_user_equipment_id, new_off = decode_str(data, off) + off += new_off + + self.sub_equipment_user_equipment_id, new_off = decode_str(data, off) + off += new_off + + self.skill_id = decode_int(data, off) + off += INT_OFF + self.text_chara_message_id = decode_int(data, off) + off += INT_OFF + + self.holographic_flag = decode_byte(data, off) + off += BYTE_OFF + self.execute_print_type = PrintType(decode_byte(data, off)) + off += BYTE_OFF + +class SaoDischargeProfileCardResponse(SaoBaseResponse): + def __init__(self, serial: str) -> None: + super().__init__(GameconnectCmd.DISCHARGE_PROFILE_CARD_RESPONSE) + self.result = 1 + self.profile_card_code = serial + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.profile_card_code) + +class SaoDischargeResourceCardRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.holographic_flag = decode_byte(data, off) + off += BYTE_OFF + + self.execute_print_type = PrintType(decode_byte(data, off)) + off += BYTE_OFF + + self.common_reward_user_data: List[MaterialCommonRewardUserData] = [] # typing lol + self.common_reward_user_data, new_off = decode_arr_cls(data, off, MaterialCommonRewardUserData) + off += new_off + +class SaoDischargeResourceCardResponse(SaoBaseResponse): + def __init__(self, serial: str) -> None: + super().__init__(GameconnectCmd.DISCHARGE_RESOURCE_CARD_RESPONSE) + self.result = 1 + self.profile_card_code = serial + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_str(self.profile_card_code) + +class SaoDischargeResourceCardCompleteRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.resource_card_code, new_off = decode_str(data, off) + off += new_off class SaoScanQrQuestProfileCardRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.profile_card_code, new_off = decode_str(data, off) + off += new_off class SaoScanQrQuestProfileCardResponse(SaoBaseResponse): - def __init__(self, cmd) -> None: - super().__init__(cmd) + def __init__(self) -> None: + super().__init__(GameconnectCmd.SCAN_QR_QUEST_PROFILE_CARD_RESPONSE) self.result = 1 - - # read_profile_card_data - self.profile_card_code = "1234123412341234123" # ID of the QR code - self.nick_name = "PLAYER" - self.rank_num = 1 #short - self.setting_title_id = 20005 #int - self.skill_id = 0 #short - self.hero_log_hero_log_id = 118000230 #int - self.hero_log_log_level = 1 #short - self.hero_log_awakening_stage = 1 #short - - self.hero_log_property1_property_id = 0 #int - self.hero_log_property1_value1 = 0 #int - self.hero_log_property1_value2 = 0 #int - self.hero_log_property2_property_id = 0 #int - self.hero_log_property2_value1 = 0 #int - self.hero_log_property2_value2 = 0 #int - self.hero_log_property3_property_id = 0 #int - self.hero_log_property3_value1 = 0 #int - self.hero_log_property3_value2 = 0 #int - self.hero_log_property4_property_id = 0 #int - self.hero_log_property4_value1 = 0 #int - self.hero_log_property4_value2 = 0 #int - - self.main_weapon_equipment_id = 0 #int - self.main_weapon_enhancement_value = 0 #short - self.main_weapon_awakening_stage = 0 #short - - self.main_weapon_property1_property_id = 0 #int - self.main_weapon_property1_value1 = 0 #int - self.main_weapon_property1_value2 = 0 #int - self.main_weapon_property2_property_id = 0 #int - self.main_weapon_property2_value1 = 0 #int - self.main_weapon_property2_value2 = 0 #int - self.main_weapon_property3_property_id = 0 #int - self.main_weapon_property3_value1 = 0 #int - self.main_weapon_property3_value2 = 0 #int - self.main_weapon_property4_property_id = 0 #int - self.main_weapon_property4_value1 = 0 #int - self.main_weapon_property4_value2 = 0 #int - - self.sub_equipment_equipment_id = 0 #int - self.sub_equipment_enhancement_value = 0 #short - self.sub_equipment_awakening_stage = 0 #short - - self.sub_equipment_property1_property_id = 0 #int - self.sub_equipment_property1_value1 = 0 #int - self.sub_equipment_property1_value2 = 0 #int - self.sub_equipment_property2_property_id = 0 #int - self.sub_equipment_property2_value1 = 0 #int - self.sub_equipment_property2_value2 = 0 #int - self.sub_equipment_property3_property_id = 0 #int - self.sub_equipment_property3_value1 = 0 #int - self.sub_equipment_property3_value2 = 0 #int - self.sub_equipment_property4_property_id = 0 #int - self.sub_equipment_property4_value1 = 0 #int - self.sub_equipment_property4_value2 = 0 #int - - self.holographic_flag = 1 #byte + self.profile_card_data: List[ReadProfileCard] = [] def make(self) -> bytes: - #new stuff + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.profile_card_data) - read_profile_card_data_struct = Struct( - "profile_card_code_size" / Int32ub, # big endian - "profile_card_code" / Int16ul[len(self.profile_card_code)], - "nick_name_size" / Int32ub, # big endian - "nick_name" / Int16ul[len(self.nick_name)], - "rank_num" / Int16ub, #short - "setting_title_id" / Int32ub, #int - "skill_id" / Int16ub, #short - "hero_log_hero_log_id" / Int32ub, #int - "hero_log_log_level" / Int16ub, #short - "hero_log_awakening_stage" / Int16ub, #short - - "hero_log_property1_property_id" / Int32ub, #int - "hero_log_property1_value1" / Int32ub, #int - "hero_log_property1_value2" / Int32ub, #int - "hero_log_property2_property_id" / Int32ub, #int - "hero_log_property2_value1" / Int32ub, #int - "hero_log_property2_value2" / Int32ub, #int - "hero_log_property3_property_id" / Int32ub, #int - "hero_log_property3_value1" / Int32ub, #int - "hero_log_property3_value2" / Int32ub, #int - "hero_log_property4_property_id" / Int32ub, #int - "hero_log_property4_value1" / Int32ub, #int - "hero_log_property4_value2" / Int32ub, #int - - "main_weapon_equipment_id" / Int32ub, #int - "main_weapon_enhancement_value" / Int16ub, #short - "main_weapon_awakening_stage" / Int16ub, #short - - "main_weapon_property1_property_id" / Int32ub, #int - "main_weapon_property1_value1" / Int32ub, #int - "main_weapon_property1_value2" / Int32ub, #int - "main_weapon_property2_property_id" / Int32ub, #int - "main_weapon_property2_value1" / Int32ub, #int - "main_weapon_property2_value2" / Int32ub, #int - "main_weapon_property3_property_id" / Int32ub, #int - "main_weapon_property3_value1" / Int32ub, #int - "main_weapon_property3_value2" / Int32ub, #int - "main_weapon_property4_property_id" / Int32ub, #int - "main_weapon_property4_value1" / Int32ub, #int - "main_weapon_property4_value2" / Int32ub, #int - - "sub_equipment_equipment_id" / Int32ub, #int - "sub_equipment_enhancement_value" / Int16ub, #short - "sub_equipment_awakening_stage" / Int16ub, #short - - "sub_equipment_property1_property_id" / Int32ub, #int - "sub_equipment_property1_value1" / Int32ub, #int - "sub_equipment_property1_value2" / Int32ub, #int - "sub_equipment_property2_property_id" / Int32ub, #int - "sub_equipment_property2_value1" / Int32ub, #int - "sub_equipment_property2_value2" / Int32ub, #int - "sub_equipment_property3_property_id" / Int32ub, #int - "sub_equipment_property3_value1" / Int32ub, #int - "sub_equipment_property3_value2" / Int32ub, #int - "sub_equipment_property4_property_id" / Int32ub, #int - "sub_equipment_property4_value1" / Int32ub, #int - "sub_equipment_property4_value2" / Int32ub, #int - - "holographic_flag" / Int8ul, # result is either 0 or 1 - - ) - - # create a resp struct - resp_struct = Struct( - "result" / Int8ul, # result is either 0 or 1 - "read_profile_card_data_size" / Rebuild(Int32ub, len_(this.read_profile_card_data)), # big endian - "read_profile_card_data" / Array(this.read_profile_card_data_size, read_profile_card_data_struct), - ) - - resp_data = resp_struct.parse(resp_struct.build(dict( - result=self.result, - read_profile_card_data_size=0, - read_profile_card_data=[], - ))) - - hero_data = dict( - profile_card_code_size=len(self.profile_card_code) * 2, - profile_card_code=[ord(x) for x in self.profile_card_code], - nick_name_size=len(self.nick_name) * 2, - nick_name=[ord(x) for x in self.nick_name], - - rank_num=self.rank_num, - setting_title_id=self.setting_title_id, - skill_id=self.skill_id, - hero_log_hero_log_id=self.hero_log_hero_log_id, - hero_log_log_level=self.hero_log_log_level, - hero_log_awakening_stage=self.hero_log_awakening_stage, - - hero_log_property1_property_id=self.hero_log_property1_property_id, - hero_log_property1_value1=self.hero_log_property1_value1, - hero_log_property1_value2=self.hero_log_property1_value2, - hero_log_property2_property_id=self.hero_log_property2_property_id, - hero_log_property2_value1=self.hero_log_property2_value1, - hero_log_property2_value2=self.hero_log_property2_value2, - hero_log_property3_property_id=self.hero_log_property3_property_id, - hero_log_property3_value1=self.hero_log_property3_value1, - hero_log_property3_value2=self.hero_log_property3_value2, - hero_log_property4_property_id=self.hero_log_property4_property_id, - hero_log_property4_value1=self.hero_log_property4_value1, - hero_log_property4_value2=self.hero_log_property4_value2, - - main_weapon_equipment_id=self.main_weapon_equipment_id, - main_weapon_enhancement_value=self.main_weapon_enhancement_value, - main_weapon_awakening_stage=self.main_weapon_awakening_stage, - - main_weapon_property1_property_id=self.main_weapon_property1_property_id, - main_weapon_property1_value1=self.main_weapon_property1_value1, - main_weapon_property1_value2=self.main_weapon_property1_value2, - main_weapon_property2_property_id=self.main_weapon_property2_property_id, - main_weapon_property2_value1=self.main_weapon_property2_value1, - main_weapon_property2_value2=self.main_weapon_property2_value2, - main_weapon_property3_property_id=self.main_weapon_property3_property_id, - main_weapon_property3_value1=self.main_weapon_property3_value1, - main_weapon_property3_value2=self.main_weapon_property3_value2, - main_weapon_property4_property_id=self.main_weapon_property4_property_id, - main_weapon_property4_value1=self.main_weapon_property4_value1, - main_weapon_property4_value2=self.main_weapon_property4_value2, - - sub_equipment_equipment_id=self.sub_equipment_equipment_id, - sub_equipment_enhancement_value=self.sub_equipment_enhancement_value, - sub_equipment_awakening_stage=self.sub_equipment_awakening_stage, - - sub_equipment_property1_property_id=self.sub_equipment_property1_property_id, - sub_equipment_property1_value1=self.sub_equipment_property1_value1, - sub_equipment_property1_value2=self.sub_equipment_property1_value2, - sub_equipment_property2_property_id=self.sub_equipment_property2_property_id, - sub_equipment_property2_value1=self.sub_equipment_property2_value1, - sub_equipment_property2_value2=self.sub_equipment_property2_value2, - sub_equipment_property3_property_id=self.sub_equipment_property3_property_id, - sub_equipment_property3_value1=self.sub_equipment_property3_value1, - sub_equipment_property3_value2=self.sub_equipment_property3_value2, - sub_equipment_property4_property_id=self.sub_equipment_property4_property_id, - sub_equipment_property4_value1=self.sub_equipment_property4_value1, - sub_equipment_property4_value2=self.sub_equipment_property4_value2, - - holographic_flag=self.holographic_flag, - ) +class SaoScanQrShopResourceCardRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off - resp_data.read_profile_card_data.append(hero_data) + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.resource_card_code, new_off = decode_str(data, off) + off += new_off - resp_data["read_profile_card_data_size"] = len(resp_data.read_profile_card_data) +class SaoScanQrQuestResourceCardRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.resource_card_code, new_off = decode_str(data, off) + off += new_off - # finally, rebuild the resp_data - resp_data = resp_struct.build(resp_data) - - self.length = len(resp_data) - return super().make() + resp_data +class SaoScanQrQuestResourceCardResponse(SaoBaseResponse): + def __init__(self, reward_type: int = 0, reward_id: int = 0, is_holo: bool = False) -> None: + super().__init__(GameconnectCmd.SCAN_QR_QUEST_RESOURCE_CARD_RESPONSE) + self.result = 1 + self.common_reward_type = reward_type + self.common_reward_id = reward_id + self.holographic_flag = is_holo + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_short(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_bool(self.holographic_flag) + class SaoConsumeCreditGuestRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) @@ -3047,7 +1694,7 @@ class SaoConsumeCreditGuestRequest(SaoBaseRequest): self.cab_type = decode_byte(data, off) off += BYTE_OFF - self.act_type = decode_byte(data, off) + self.act_type = ActTypeConsumeCredit(decode_byte(data, off)) off += BYTE_OFF self.consume_num = decode_byte(data, off) @@ -3065,20 +1712,14 @@ class SaoChangePartyRequest(SaoBaseRequest): self.user_id = user_id[0] off += user_id[1] - self.act_type = decode_byte(data, off) + self.act_type = ActTypeChangeParty(decode_byte(data, off)) off += BYTE_OFF - self.party_data_count = decode_int(data, off) - off += INT_OFF - self.party_data_list: List[PartyData] = [] + self.party_data_list, new_off = decode_arr_cls(data, off, PartyData) + off += new_off - for _ in range(self.party_data_count): - tmp = PartyData(data, off) - self.party_data_list.append(tmp) - off += tmp.get_size() - -class TrialTowerPlayEndUnanalyzedLogFixed(SaoBaseRequest): +class SaoTrialTowerPlayEndUnanalyzedLogFixedRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) off = 0 @@ -3096,17 +1737,20 @@ class TrialTowerPlayEndUnanalyzedLogFixed(SaoBaseRequest): self.rarity_up_exec_flag = decode_byte(data, off) off += BYTE_OFF -class GetShopResourceSalesDataListRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - off = 0 - user_id = decode_str(data, off) - self.user_id = user_id[0] - off += user_id[1] +class SaoTrialTowerPlayEndUnanalyzedLogFixedResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.TRIAL_TOWER_PLAY_END_UNANALYZED_LOG_FIXED_RESPONSE) + self.result = 1 + self.play_end_unanalyzed_log_reward_data_list: List[QuestScenePlayEndUnanalyzedLogRewardData] = [] -class GetShopResourceSalesDataListResponse(SaoBaseResponse): - def __init__(self, cmd_id: int) -> None: - super().__init__(cmd_id) + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.play_end_unanalyzed_log_reward_data_list) + +class SaoGetShopResourceSalesDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_SHOP_RESOURCE_SALES_DATA_LIST_RESPONSE) self.result = 1 # byte self.shop_resource_sales_data: List[ShopResourceSalesData] = [] @@ -3117,6 +1761,19 @@ class GetShopResourceSalesDataListResponse(SaoBaseResponse): self.header.length = len(ret) return super().make() + ret +class SaoPurchaseShopResourceRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.user_shop_resource_id, new_off = decode_str(data, off) + off += new_off + class GetYuiMedalShopUserDataListRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) @@ -3159,78 +1816,6 @@ class GetGashaMedalShopUserDataListResponse(SaoBaseResponse): self.header.length = len(ret) return super().make() + ret -class GetMYuiMedalShopDataRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.dummy = decode_byte(data, 0) - -class GetMYuiMedalShopDataResponse(SaoBaseResponse): - def __init__(self, cmd_id: int) -> None: - super().__init__(cmd_id) - self.result = 1 # byte - self.data_list: List[YuiMedalShopData] = [] - - def make(self) -> bytes: - ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) - - self.header.length = len(ret) - return super().make() + ret - -class GetMYuiMedalShopItemsRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.dummy = decode_byte(data, 0) - -class GetMYuiMedalShopItemsResponse(SaoBaseResponse): - def __init__(self, cmd_id: int) -> None: - super().__init__(cmd_id) - self.result = 1 # byte - self.data_list: List[YuiMedalShopItemData] = [] - - def make(self) -> bytes: - ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) - - self.header.length = len(ret) - return super().make() + ret - -class GetMGashaMedalShopsRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.dummy = decode_byte(data, 0) - -class GetMGashaMedalShopsResponse(SaoBaseResponse): - def __init__(self, cmd_id: int) -> None: - super().__init__(cmd_id) - self.result = 1 # byte - self.data_list: List[GashaMedalShop] = [] - - def make(self) -> bytes: - ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) - - self.header.length = len(ret) - return super().make() + ret - -class GetMResEarnCampaignShopsRequest(SaoBaseRequest): - def __init__(self, header: SaoRequestHeader, data: bytes) -> None: - super().__init__(header, data) - self.dummy = decode_byte(data, 0) - -class GetMResEarnCampaignShopsResponse(SaoBaseResponse): - def __init__(self, cmd_id: int) -> None: - super().__init__(cmd_id) - self.result = 1 # byte - self.data_list: List[ResEarnCampaignShop] = [] - - def make(self) -> bytes: - ret = encode_byte(self.result) - ret += encode_arr_cls(self.data_list) - - self.header.length = len(ret) - return super().make() + ret - class SaoGiveFreeTicketRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) @@ -3244,15 +1829,6 @@ class SaoGiveFreeTicketRequest(SaoBaseRequest): self.give_num = decode_byte(data, off) off += BYTE_OFF -class SaoGiveFreeTicketResponse(SaoBaseResponse): - def __init__(self, cmd_id: int) -> None: - super().__init__(cmd_id) - self.result = 1 # byte - - def make(self) -> bytes: - ret = encode_byte(self.result) - return super().make() + ret - class SaoLogoutTicketUnpurchasedRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) @@ -3266,15 +1842,6 @@ class SaoLogoutTicketUnpurchasedRequest(SaoBaseRequest): self.cabinet_type = decode_byte(data, off) off += BYTE_OFF -class SaoLogoutTicketUnpurchasedResponse(SaoBaseResponse): - def __init__(self, cmd_id: int) -> None: - super().__init__(cmd_id) - self.result = 1 # byte - - def make(self) -> bytes: - ret = encode_byte(self.result) - return super().make() + ret - class SaoGetQuestHierarchyProgressDegreesRankingListRequest(SaoBaseRequest): def __init__(self, header: SaoRequestHeader, data: bytes) -> None: super().__init__(header, data) @@ -3322,3 +1889,2337 @@ class SaoGetQuestPopularHeroLogRankingListResponse(SaoBaseResponse): ret = encode_byte(self.result) ret += encode_arr_cls(self.quest_popular_hero_log_ranking_data_list) return super().make() + ret + +class SaoGetVariousTutorialDataListResponse(SaoBaseResponse): + def __init__(self) -> None: + super().__init__(GameconnectCmd.GET_VARIOUS_TUTORIAL_DATA_LIST_RESPONSE) + self.result = 1 + self.end_tutorial_type_list: List[int] = [] + + def make(self) -> bytes: + ret = encode_byte(self.result) + ret += encode_arr_num(self.end_tutorial_type_list, BYTE_OFF) + return super().make() + ret + +class SaoVariousTutorialEndRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ticket_id, new_off = decode_str(data, off) + off += new_off + self.user_id, new_off = decode_str(data, off) + off += new_off + self.tutorial_type = decode_byte(data, off) + off += BYTE_OFF + +class SaoGetMExTowersRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ex_tower_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMExTowerQuestsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.ex_tower_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMChatEventStoriesRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.event_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMTreasureHuntsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.treasure_hunt_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMTreasureHuntWholeTasksRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.treasure_hunt_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMTreasureHuntIndividualTasksRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.treasure_hunt_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMTreasureHuntSpecialEffectsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.treasure_hunt_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMTreasureHuntEventPointRewardCommonRewardsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.treasure_hunt_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMTreasureHuntEventPointRewardTitlesRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.treasure_hunt_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMDefragMatchesRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.defrag_match_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMDefragMatchSeedRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.defrag_match_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMDefragMatchSpecialEffectsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.defrag_match_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMDefragMatchGradesRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.defrag_match_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMDefragMatchPeriodBonusesRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.defrag_match_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMDefragMatchRandomBonusTablesRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.defrag_match_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMDefragMatchRareDropsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.defrag_match_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMEventScenesRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.event_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMBeginnerMissionConditionsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.beginner_mission_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMBeginnerMissionRewardsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.beginner_mission_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMBeginnerMissionSeatConditionsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.beginner_mission_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMBeginnerMissionSeatRewardsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.beginner_mission_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMEventMonstersRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.event_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMGashaMedalSettingsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.gasha_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMGashaMedalShopItemsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.gasha_medal_shop_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMResEarnCampaignApplicationProductsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.res_earn_campaign_application_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMResEarnCampaignShopItemsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.res_earn_campaign_shop_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMPlayCampaignRewardsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.play_campaign_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMLinkedSiteRegCampaignRewardsRequest(SaoBaseRequest): + def __init__(self, header: SaoRequestHeader, data: bytes) -> None: + super().__init__(header, data) + off = 0 + self.linked_site_reg_campaign_id_list, new_off = decode_arr_num(data, off, INT_OFF) + off += new_off + +class SaoGetMPlayerRanksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_PLAYER_RANKS_RESPONSE) + self.result = 1 + self.data_list: List[PlayerRankData] = [] + + if data_list: + for item in data_list: + self.data_list.append(PlayerRankData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTitlesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TITLES_RESPONSE) + self.result = 1 + self.data_list: List[TitleData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TitleData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMFragmentsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_FRAGMENTS_RESPONSE) + self.result = 1 + self.data_list: List[FragmentData] = [] + + if data_list: + for item in data_list: + self.data_list.append(FragmentData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMRewardTablesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_REWARD_TABLES_RESPONSE) + self.result = 1 + self.data_list: List[RewardTableData] = [] + + if data_list: + for item in data_list: + self.data_list.append(RewardTableData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMRewardSetsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_REWARD_SETS_RESPONSE) + self.result = 1 + self.data_list: List[RewardSetData] = [] + + if data_list: + for item in data_list: + self.data_list.append(RewardSetData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMUnanalyzedLogGradesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_UNANALYZED_LOG_GRADES_RESPONSE) + self.result = 1 + self.data_list: List[UnanalyzedLogGradeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(UnanalyzedLogGradeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMAppointLeaderParamsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_APPOINT_LEADER_PARAMS_RESPONSE) + self.result = 1 + self.data_list: List[AppointLeaderParamData] = [] + + if data_list: + for item in data_list: + self.data_list.append(AppointLeaderParamData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMAppointLeaderEffectsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_APPOINT_LEADER_EFFECTS_RESPONSE) + self.result = 1 + self.data_list: List[AppointLeaderEffectData] = [] + + if data_list: + for item in data_list: + self.data_list.append(AppointLeaderEffectData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMAppointLeaderEffectTypesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_APPOINT_LEADER_EFFECT_TYPES_RESPONSE) + self.result = 1 + self.data_list: List[AppointLeaderEffectTypeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(AppointLeaderEffectTypeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMRaritiesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_RARITIES_RESPONSE) + self.result = 1 + self.data_list: List[RarityData] = [] + + if data_list: + for item in data_list: + self.data_list.append(RarityData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMCompositionEventsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_COMPOSITION_EVENTS_RESPONSE) + self.result = 1 + self.data_list: List[CompositionEventData] = [] + + if data_list: + for item in data_list: + self.data_list.append(CompositionEventData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMCompositionParamsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_COMPOSITION_PARAMS_RESPONSE) + self.result = 1 + self.data_list: List[CompositionParamData] = [] + + if data_list: + for item in data_list: + self.data_list.append(CompositionParamData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGamePlayPricesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GAME_PLAY_PRICES_RESPONSE) + self.result = 1 + self.data_list: List[GamePlayPriceData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GamePlayPriceData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBuyTicketsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BUY_TICKETS_RESPONSE) + self.result = 1 + self.data_list: List[BuyTicketData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BuyTicketData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTipsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TIPS_RESPONSE) + self.result = 1 + self.data_list: List[TipsData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TipsData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMCapsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_CAPS_RESPONSE) + self.result = 1 + self.data_list: List[CapData] = [] + + if data_list: + for item in data_list: + self.data_list.append(CapData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMHeroLogResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_HERO_LOG_RESPONSE) + self.result = 1 + self.data_list: List[HeroLogData] = [] + + if data_list: + for item in data_list: + self.data_list.append(HeroLogData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMHeroLogLevelsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_HERO_LOG_LEVELS_RESPONSE) + self.result = 1 + self.data_list: List[HeroLogLevelData] = [] + + if data_list: + for item in data_list: + self.data_list.append(HeroLogLevelData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMHeroLogRolesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_HERO_LOG_ROLES_RESPONSE) + self.result = 1 + self.data_list: List[HeroLogRoleData] = [] + + if data_list: + for item in data_list: + self.data_list.append(HeroLogRoleData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMHeroLogTrustRanksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_HERO_LOG_TRUST_RANKS_RESPONSE) + self.result = 1 + self.data_list: List[HeroLogTrustRankData] = [] + + if data_list: + for item in data_list: + self.data_list.append(HeroLogTrustRankData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMCharasResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_CHARAS_RESPONSE) + self.result = 1 + self.data_list: List[CharaData] = [] + + if data_list: + for item in data_list: + self.data_list.append(CharaData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMCharaFriendlyRanksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_CHARA_FRIENDLY_RANKS_RESPONSE) + self.result = 1 + self.data_list: List[CharaFriendlyRankData] = [] + + if data_list: + for item in data_list: + self.data_list.append(CharaFriendlyRankData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEquipmentsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EQUIPMENTS_RESPONSE) + self.result = 1 + self.data_list: List[EquipmentData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EquipmentData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEquipmentLevelsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EQUIPMENT_LEVELS_RESPONSE) + self.result = 1 + self.data_list: List[EquipmentLevelData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EquipmentLevelData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMWeaponTypesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_WEAPON_TYPES_RESPONSE) + self.result = 1 + self.data_list: List[WeaponTypeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(WeaponTypeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMItemsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_ITEMS_RESPONSE) + self.result = 1 + self.data_list: List[ItemData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ItemData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMItemTypesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_ITEM_TYPES_RESPONSE) + self.result = 1 + self.data_list: List[ItemTypeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ItemTypeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBuffItemsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BUFF_ITEMS_RESPONSE) + self.result = 1 + self.data_list: List[BuffItemData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BuffItemData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEnemiesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_ENEMIES_RESPONSE) + self.result = 1 + self.data_list: List[EnemyData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EnemyData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEnemySetsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_ENEMY_SETS_RESPONSE) + self.result = 1 + self.data_list: List[EnemySetData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EnemySetData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEnemyKindsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_ENEMY_KINDS_RESPONSE) + self.result = 1 + self.data_list: List[EnemyKindData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EnemyKindData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEnemyCategoriesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_ENEMY_CATEGORIES_RESPONSE) + self.result = 1 + self.data_list: List[EnemyCategoryData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EnemyCategoryData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMUnitsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_UNITS_RESPONSE) + self.result = 1 + self.data_list: List[UnitData] = [] + + if data_list: + for item in data_list: + self.data_list.append(UnitData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMUnitGimmicksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_UNIT_GIMMICKS_RESPONSE) + self.result = 1 + self.data_list: List[UnitGimmickData] = [] + + if data_list: + for item in data_list: + self.data_list.append(UnitGimmickData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMUnitCollisionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_UNIT_COLLISIONS_RESPONSE) + self.result = 1 + self.data_list: List[UnitCollisionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(UnitCollisionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMUnitPowersResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_UNIT_POWERS_RESPONSE) + self.result = 1 + self.data_list: List[UnitPowerData] = [] + + if data_list: + for item in data_list: + self.data_list.append(UnitPowerData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGimmickAttacksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GIMMICK_ATTACKS_RESPONSE) + self.result = 1 + self.data_list: List[GimmickAttackData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GimmickAttackData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMCharaAttacksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_CHARA_ATTACKS_RESPONSE) + self.result = 1 + self.data_list: List[CharaAttackData] = [] + + if data_list: + for item in data_list: + self.data_list.append(CharaAttackData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBossAttacksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BOSS_ATTACKS_RESPONSE) + self.result = 1 + self.data_list: List[BossAttackData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BossAttackData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMMonsterAttacksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_MONSTER_ATTACKS_RESPONSE) + self.result = 1 + self.data_list: List[MonsterAttackData] = [] + + if data_list: + for item in data_list: + self.data_list.append(MonsterAttackData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMMonsterActionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_MONSTER_ACTIONS_RESPONSE) + self.result = 1 + self.data_list: List[MonsterActionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(MonsterActionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMPropertiesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_PROPERTIES_RESPONSE) + self.result = 1 + self.data_list: List[PropertyData] = [] + + if data_list: + for item in data_list: + self.data_list.append(PropertyData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMPropertyTablesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_PROPERTY_TABLES_RESPONSE) + self.result = 1 + self.data_list: List[PropertyTableData] = [] + + if data_list: + for item in data_list: + self.data_list.append(PropertyTableData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMPropertyTypesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_PROPERTY_TYPES_RESPONSE) + self.result = 1 + self.data_list: List[PropertyTypeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(PropertyTypeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMSkillsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_SKILLS_RESPONSE) + self.result = 1 + self.data_list: List[SkillData] = [] + + if data_list: + for item in data_list: + self.data_list.append(SkillData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMSkillTablesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_SKILL_TABLES_RESPONSE) + self.result = 1 + self.data_list: List[SkillTableData] = [] + + if data_list: + for item in data_list: + self.data_list.append(SkillTableData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMSkillLevelsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_SKILL_LEVELS_RESPONSE) + self.result = 1 + self.data_list: List[SkillLevelData] = [] + + if data_list: + for item in data_list: + self.data_list.append(SkillLevelData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMAwakeningsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_AWAKENINGS_RESPONSE) + self.result = 1 + self.data_list: List[AwakeningData] = [] + + if data_list: + for item in data_list: + self.data_list.append(AwakeningData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMSynchroSkillsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_SYNCHRO_SKILLS_RESPONSE) + self.result = 1 + self.data_list: List[SynchroSkillData] = [] + + if data_list: + for item in data_list: + self.data_list.append(SynchroSkillData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMSoundSkillCutInVoicesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_SOUND_SKILL_CUT_IN_VOICES_RESPONSE) + self.result = 1 + self.data_list: List[SoundSkillCutInVoiceData] = [] + + if data_list: + for item in data_list: + self.data_list.append(SoundSkillCutInVoiceData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestScenesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_SCENES_RESPONSE) + self.result = 1 + self.data_list: List[QuestSceneData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestSceneData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestExistUnitsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_EXIST_UNITS_RESPONSE) + self.result = 1 + self.data_list: List[QuestExistUnitData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestExistUnitData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestEpisodeAppendRewardsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_EPISODE_APPEND_REWARDS_RESPONSE) + self.result = 1 + self.data_list: List[QuestEpisodeAppendRewardData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestEpisodeAppendRewardData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMSideQuestsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_SIDE_QUESTS_RESPONSE) + self.result = 1 + self.data_list: List[SideQuestData] = [] + + if data_list: + for item in data_list: + self.data_list.append(SideQuestData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEpisodesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EPISODES_RESPONSE) + self.result = 1 + self.data_list: List[EpisodeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EpisodeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEpisodeChaptersResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EPISODE_CHAPTERS_RESPONSE) + self.result = 1 + self.data_list: List[EpisodeChapterData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EpisodeChapterData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEpisodePartsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EPISODE_PARTS_RESPONSE) + self.result = 1 + self.data_list: List[EpisodePartData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EpisodePartData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTrialTowersResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TRIAL_TOWERS_RESPONSE) + self.result = 1 + self.data_list: List[TrialTowerData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TrialTowerData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMExTowersResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EX_TOWERS_RESPONSE) + self.result = 1 + self.data_list: List[ExTowerData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ExTowerData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMExTowerQuestsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EX_TOWER_QUESTS_RESPONSE) + self.result = 1 + self.data_list: List[ExTowerQuestData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ExTowerQuestData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMMenuDisplayEnemiesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_MENU_DISPLAY_ENEMIES_RESPONSE) + self.result = 1 + self.data_list: List[MenuDisplayEnemyData] = [] + + if data_list: + for item in data_list: + self.data_list.append(MenuDisplayEnemyData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMMissionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_MISSIONS_RESPONSE) + self.result = 1 + self.data_list: List[MissionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(MissionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMMissionTablesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_MISSION_TABLES_RESPONSE) + self.result = 1 + self.data_list: List[MissionTableData] = [] + + if data_list: + for item in data_list: + self.data_list.append(MissionTableData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMMissionDifficultiesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_MISSION_DIFFICULTIES_RESPONSE) + self.result = 1 + self.data_list: List[MissionDifficultyData] = [] + + if data_list: + for item in data_list: + self.data_list.append(MissionDifficultyData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBattleCamerasResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BATTLE_CAMERAS_RESPONSE) + self.result = 1 + self.data_list: List[BattleCameraData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BattleCameraData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMChatMainStoriesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_CHAT_MAIN_STORIES_RESPONSE) + self.result = 1 + self.data_list: List[ChatMainStoryData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ChatMainStoryData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMChatSideStoriesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_CHAT_SIDE_STORIES_RESPONSE) + self.result = 1 + self.data_list: List[ChatSideStoryData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ChatSideStoryData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMChatEventStoriesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_CHAT_EVENT_STORIES_RESPONSE) + self.result = 1 + self.data_list: List[ChatEventStoryData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ChatEventStoryData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMNavigatorCharasResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_NAVIGATOR_CHARAS_RESPONSE) + self.result = 1 + self.data_list: List[NavigatorCharaData] = [] + + if data_list: + for item in data_list: + self.data_list.append(NavigatorCharaData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMNavigatorCommentsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_NAVIGATOR_COMMENTS_RESPONSE) + self.result = 1 + self.data_list: List[NavigatorCommentData] = [] + + if data_list: + for item in data_list: + self.data_list.append(NavigatorCommentData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMExBonusTablesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EX_BONUS_TABLES_RESPONSE) + self.result = 1 + self.data_list: List[ExBonusTableData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ExBonusTableData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMExBonusConditionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EX_BONUS_CONDITIONS_RESPONSE) + self.result = 1 + self.data_list: List[ExBonusConditionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ExBonusConditionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestRareDropsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_RARE_DROPS_RESPONSE) + self.result = 1 + self.data_list: List[QuestRareDropData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestRareDropData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestSpecialRareDropSettingsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_SPECIAL_RARE_DROP_SETTINGS_RESPONSE) + self.result = 1 + self.data_list: List[QuestSpecialRareDropSettingData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestSpecialRareDropSettingData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestSpecialRareDropsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_SPECIAL_RARE_DROPS_RESPONSE) + self.result = 1 + self.data_list: List[QuestSpecialRareDropData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestSpecialRareDropData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestTutorialsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_TUTORIALS_RESPONSE) + self.result = 1 + self.data_list: List[QuestTutorialData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestTutorialData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestPlayerTraceTablesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_PLAYER_TRACE_TABLES_RESPONSE) + self.result = 1 + self.data_list: List[QuestPlayerTraceTableData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestPlayerTraceTableData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestStillsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_STILLS_RESPONSE) + self.result = 1 + self.data_list: List[QuestStillData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestStillData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashasResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHAS_RESPONSE) + self.result = 1 + self.data_list: List[GashaData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaHeadersResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_HEADERS_RESPONSE) + self.result = 1 + self.data_list: List[GashaHeaderData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaHeaderData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaLotteryRaritiesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_LOTTERY_RARITIES_RESPONSE) + self.result = 1 + self.data_list: List[GashaLotteryRarityData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaLotteryRarityData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaPrizesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_PRIZES_RESPONSE) + self.result = 1 + self.data_list: List[GashaPrizeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaPrizeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMComebackEventsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_COMEBACK_EVENTS_RESPONSE) + self.result = 1 + self.data_list: List[ComebackEventData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ComebackEventData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMAdBannersResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_AD_BANNERS_RESPONSE) + self.result = 1 + self.data_list: List[AdBannerData] = [] + + if data_list: + for item in data_list: + self.data_list.append(AdBannerData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEventsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EVENTS_RESPONSE) + self.result = 1 + self.data_list: List[EventsData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EventsData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTreasureHuntsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TREASURE_HUNTS_RESPONSE) + self.result = 1 + self.data_list: List[TreasureHuntsData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TreasureHuntsData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTreasureHuntWholeTasksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TREASURE_HUNT_WHOLE_TASKS_RESPONSE) + self.result = 1 + self.data_list: List[TreasureHuntWholeTasksData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TreasureHuntWholeTasksData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTreasureHuntIndividualTasksResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TREASURE_HUNT_INDIVIDUAL_TASKS_RESPONSE) + self.result = 1 + self.data_list: List[TreasureHuntIndividualTasksData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TreasureHuntIndividualTasksData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTreasureHuntSpecialEffectsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TREASURE_HUNT_SPECIAL_EFFECTS_RESPONSE) + self.result = 1 + self.data_list: List[TreasureHuntSpecialEffectsData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TreasureHuntSpecialEffectsData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTreasureHuntEventPointRewardCommonRewardsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TREASURE_HUNT_EVENT_POINT_REWARD_COMMON_REWARDS_RESPONSE) + self.result = 1 + self.data_list: List[TreasureHuntEventPointRewardCommonRewardsData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TreasureHuntEventPointRewardCommonRewardsData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTreasureHuntEventPointRewardTitlesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TREASURE_HUNT_EVENT_POINT_REWARD_TITLES_RESPONSE) + self.result = 1 + self.data_list: List[TreasureHuntEventPointRewardTitlesData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TreasureHuntEventPointRewardTitlesData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMTreasureHuntTaskTextsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_TREASURE_HUNT_TASK_TEXTS_RESPONSE) + self.result = 1 + self.data_list: List[TreasureHuntTaskTextData] = [] + + if data_list: + for item in data_list: + self.data_list.append(TreasureHuntTaskTextData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBnidSerialCodesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BNID_SERIAL_CODES_RESPONSE) + self.result = 1 + self.data_list: List[BnidSerialCodeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BnidSerialCodeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBnidSerialCodeRewardsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BNID_SERIAL_CODE_REWARDS_RESPONSE) + self.result = 1 + self.data_list: List[BnidSerialCodeRewardData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BnidSerialCodeRewardData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMSupportLogResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_SUPPORT_LOG_RESPONSE) + self.result = 1 + self.data_list: List[SupportLogData] = [] + + if data_list: + for item in data_list: + self.data_list.append(SupportLogData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMSupportLogTypesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_SUPPORT_LOG_TYPES_RESPONSE) + self.result = 1 + self.data_list: List[SupportLogTypeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(SupportLogTypeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEpisodeAppendsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EPISODE_APPENDS_RESPONSE) + self.result = 1 + self.data_list: List[EpisodeAppendData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EpisodeAppendData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestDefragMatchQuestsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_DEFRAG_MATCH_QUESTS_RESPONSE) + self.result = 1 + self.data_list: List[QuestDefragMatchQuestData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestDefragMatchQuestData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestDefragMatchQuestBossTablesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_DEFRAG_MATCH_QUEST_BOSS_TABLES_RESPONSE) + self.result = 1 + self.data_list: List[QuestDefragMatchQuestBossTableData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestDefragMatchQuestBossTableData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCHES_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchSeedResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_SEED_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchSeedData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchSeedData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchSpecialEffectsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_SPECIAL_EFFECTS_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchSpecialEffectData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchSpecialEffectData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchGradesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_GRADES_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchGradeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchGradeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchCpuUnitsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_CPU_UNITS_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchCpuUnitData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchCpuUnitData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchCpuSupportLogsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_CPU_SUPPORT_LOGS_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchCpuSupportLogData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchCpuSupportLogData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchPeriodBonusesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_PERIOD_BONUSES_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchPeriodBonusData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchPeriodBonusData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchRandomBonusTablesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_RANDOM_BONUS_TABLES_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchRandomBonusTableData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchRandomBonusTableData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchRandomBonusConditionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_RANDOM_BONUS_CONDITIONS_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchRandomBonusConditionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchRandomBonusConditionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMDefragMatchRareDropsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_DEFRAG_MATCH_RARE_DROPS_RESPONSE) + self.result = 1 + self.data_list: List[DefragMatchRareDropData] = [] + + if data_list: + for item in data_list: + self.data_list.append(DefragMatchRareDropData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMYuiMedalShopsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_YUI_MEDAL_SHOPS_RESPONSE) + self.result = 1 + self.data_list: List[YuiMedalShopData] = [] + + if data_list: + for item in data_list: + self.data_list.append(YuiMedalShopData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMYuiMedalShopItemsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_YUI_MEDAL_SHOP_ITEMS_RESPONSE) + self.result = 1 + self.data_list: List[YuiMedalShopItemData] = [] + + if data_list: + for item in data_list: + self.data_list.append(YuiMedalShopItemData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEventScenesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EVENT_SCENES_RESPONSE) + self.result = 1 + self.data_list: List[EventSceneData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EventSceneData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGenericCampaignPeriodsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GENERIC_CAMPAIGN_PERIODS_RESPONSE) + self.result = 1 + self.data_list: List[GenericCampaignPeriodData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GenericCampaignPeriodData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBeginnerMissionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BEGINNER_MISSIONS_RESPONSE) + self.result = 1 + self.data_list: List[BeginnerMissionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BeginnerMissionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBeginnerMissionConditionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BEGINNER_MISSION_CONDITIONS_RESPONSE) + self.result = 1 + self.data_list: List[BeginnerMissionConditionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BeginnerMissionConditionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBeginnerMissionRewardsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BEGINNER_MISSION_REWARDS_RESPONSE) + self.result = 1 + self.data_list: List[BeginnerMissionRewardData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BeginnerMissionRewardData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBeginnerMissionSeatConditionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BEGINNER_MISSION_SEAT_CONDITIONS_RESPONSE) + self.result = 1 + self.data_list: List[BeginnerMissionSeatConditionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BeginnerMissionSeatConditionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMBeginnerMissionSeatRewardsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_BEGINNER_MISSION_SEAT_REWARDS_RESPONSE) + self.result = 1 + self.data_list: List[BeginnerMissionSeatRewardData] = [] + + if data_list: + for item in data_list: + self.data_list.append(BeginnerMissionSeatRewardData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEventItemsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EVENT_ITEMS_RESPONSE) + self.result = 1 + self.data_list: List[EventItemData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EventItemData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMEventMonstersResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_EVENT_MONSTERS_RESPONSE) + self.result = 1 + self.data_list: List[EventMonsterData] = [] + + if data_list: + for item in data_list: + self.data_list.append(EventMonsterData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMYuiMedalBonusesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_YUI_MEDAL_BONUSES_RESPONSE) + self.result = 1 + self.data_list: List[YuiMedalBonusData] = [] + + if data_list: + for item in data_list: + self.data_list.append(YuiMedalBonusData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMYuiMedalBonusConditionsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_YUI_MEDAL_BONUS_CONDITIONS_RESPONSE) + self.result = 1 + self.data_list: List[YuiMedalBonusConditionData] = [] + + if data_list: + for item in data_list: + self.data_list.append(YuiMedalBonusConditionData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaMedalsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_MEDALS_RESPONSE) + self.result = 1 + self.data_list: List[GashaMedalData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaMedalData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaMedalTypesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_MEDAL_TYPES_RESPONSE) + self.result = 1 + self.data_list: List[GashaMedalTypeData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaMedalTypeData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaMedalSettingsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_MEDAL_SETTINGS_RESPONSE) + self.result = 1 + self.data_list: List[GashaMedalSettingData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaMedalSettingData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaMedalBonusesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_MEDAL_BONUSES_RESPONSE) + self.result = 1 + self.data_list: List[GashaMedalBonusData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaMedalBonusData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaMedalShopsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_MEDAL_SHOPS_RESPONSE) + self.result = 1 + self.data_list: List[GashaMedalShopData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaMedalShopData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaMedalShopItemsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_MEDAL_SHOP_ITEMS_RESPONSE) + self.result = 1 + self.data_list: List[GashaMedalShopItemData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaMedalShopItemData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMResEarnCampaignApplicationsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_RES_EARN_CAMPAIGN_APPLICATIONS_RESPONSE) + self.result = 1 + self.data_list: List[ResEarnCampaignApplicationData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ResEarnCampaignApplicationData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMResEarnCampaignApplicationProductsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_RES_EARN_CAMPAIGN_APPLICATION_PRODUCTS_RESPONSE) + self.result = 1 + self.data_list: List[ResEarnCampaignApplicationProductData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ResEarnCampaignApplicationProductData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMResEarnCampaignShopsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_RES_EARN_CAMPAIGN_SHOPS_RESPONSE) + self.result = 1 + self.data_list: List[ResEarnCampaignShopData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ResEarnCampaignShopData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMResEarnCampaignShopItemsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_RES_EARN_CAMPAIGN_SHOP_ITEMS_RESPONSE) + self.result = 1 + self.data_list: List[ResEarnCampaignShopItemData] = [] + + if data_list: + for item in data_list: + self.data_list.append(ResEarnCampaignShopItemData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMPayingYuiMedalBonusesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_PAYING_YUI_MEDAL_BONUSES_RESPONSE) + self.result = 1 + self.data_list: List[PayingYuiMedalBonusData] = [] + + if data_list: + for item in data_list: + self.data_list.append(PayingYuiMedalBonusData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMAcLoginBonusesResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_AC_LOGIN_BONUSES_RESPONSE) + self.result = 1 + self.data_list: List[AcLoginBonusData] = [] + + if data_list: + for item in data_list: + self.data_list.append(AcLoginBonusData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMPlayCampaignsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_PLAY_CAMPAIGNS_RESPONSE) + self.result = 1 + self.data_list: List[PlayCampaignData] = [] + + if data_list: + for item in data_list: + self.data_list.append(PlayCampaignData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMPlayCampaignRewardsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_PLAY_CAMPAIGN_REWARDS_RESPONSE) + self.result = 1 + self.data_list: List[PlayCampaignRewardData] = [] + + if data_list: + for item in data_list: + self.data_list.append(PlayCampaignRewardData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMGashaFreeCampaignsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_GASHA_FREE_CAMPAIGNS_RESPONSE) + self.result = 1 + self.data_list: List[GashaFreeCampaignData] = [] + + if data_list: + for item in data_list: + self.data_list.append(GashaFreeCampaignData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMQuestDropBoostCampaignsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_QUEST_DROP_BOOST_CAMPAIGNS_RESPONSE) + self.result = 1 + self.data_list: List[QuestDropBoostCampaignData] = [] + + if data_list: + for item in data_list: + self.data_list.append(QuestDropBoostCampaignData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMFirstTicketPurchaseCampaignsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_FIRST_TICKET_PURCHASE_CAMPAIGNS_RESPONSE) + self.result = 1 + self.data_list: List[FirstTicketPurchaseCampaignData] = [] + + if data_list: + for item in data_list: + self.data_list.append(FirstTicketPurchaseCampaignData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMLinkedSiteRegCampaignsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_LINKED_SITE_REG_CAMPAIGNS_RESPONSE) + self.result = 1 + self.data_list: List[LinkedSiteRegCampaignsData] = [] + + if data_list: + for item in data_list: + self.data_list.append(LinkedSiteRegCampaignsData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + +class SaoGetMLinkedSiteRegCampaignRewardsResponse(SaoBaseResponse): + def __init__(self, data_list: List[Dict]) -> None: + super().__init__(GameconnectCmd.GET_M_LINKED_SITE_REG_CAMPAIGN_REWARDS_RESPONSE) + self.result = 1 + self.data_list: List[LinkedSiteRegCampaignRewardData] = [] + + if data_list: + for item in data_list: + self.data_list.append(LinkedSiteRegCampaignRewardData.from_args(item)) + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.result) \ + + encode_arr_cls(self.data_list) + diff --git a/titles/sao/handlers/helpers.py b/titles/sao/handlers/helpers.py index 97a15b2..6b91c7b 100644 --- a/titles/sao/handlers/helpers.py +++ b/titles/sao/handlers/helpers.py @@ -1,7 +1,8 @@ -from typing import Tuple, List, Optional +from typing import Tuple, List, Optional, Dict import struct import logging from datetime import datetime +from enum import IntEnum BIGINT_OFF = 16 LONG_OFF = 8 @@ -78,31 +79,64 @@ def decode_arr_str(data: bytes, offset: int) -> Tuple[List[str], int]: return (ret, size) +def decode_date_str(data: bytes, offset: int) -> Tuple[datetime, int]: + s, new_o = decode_str(data, offset) + return (prs_dt(s), new_o) + +def decode_bool(data: bytes, offset: int) -> bool: + return bool(decode_byte(data, offset)) + def encode_byte(data: int) -> bytes: - return struct.pack("!b", data) + if data is None: + return b"\0" + try: + return struct.pack("!b", int(data)) + except Exception as e: + logging.getLogger('sao').error(f"Failed to encode {data} as byte! - {e}") + return b"\0" def encode_short(data: int) -> bytes: - return struct.pack("!h", data) + if data is None: + return b"\0\0" + try: + return struct.pack("!h", int(data)) + except Exception as e: + logging.getLogger('sao').error(f"Failed to encode {data} as short! - {e}") + return b"\0\0" def encode_int(data: int) -> bytes: - return struct.pack("!i", data) + if data is None: + return b"\0\0\0\0" + try: + return struct.pack("!i", int(data)) + except Exception as e: + logging.getLogger('sao').error(f"Failed to encode {data} as int! - {e}") + return b"\0\0\0\0" def encode_long(data: int) -> bytes: - return struct.pack("!l", data) + if data is None: + return b"\0\0\0\0\0\0\0\0" + return struct.pack("!l", int(data)) def encode_bigint(data: int) -> bytes: - return struct.pack("!q", data) + if data is None: + return b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + return struct.pack("!q", int(data)) def encode_str(s: str) -> bytes: + if s is None: + return b"\0\0\0\0" try: - str_bytes = s.encode("utf-16-le", errors="replace") + str_bytes = str(s).encode("utf-16-le", errors="replace") str_len_bytes = struct.pack("!I", len(str_bytes)) return str_len_bytes + str_bytes - except: - logging.getLogger('sao').error(f"Failed to encode {s} as bytes!") + except Exception as e: + logging.getLogger('sao').error(f"Failed to encode {s} as bytes! - {e}") return b"" def encode_arr_num(data: List[int], element_size: int) -> bytes: + if data is None: + return b"\0\0\0\0" ret = encode_int(len(data)) if element_size == BYTE_OFF: @@ -126,6 +160,120 @@ def encode_arr_num(data: List[int], element_size: int) -> bytes: return ret +def encode_bool(b: bool) -> bytes: + if b is None: + return b"\0" + return encode_byte(int(b)) + +def encode_date_str(d: datetime) -> bytes: + return encode_str(fmt_dt(d)) + +class PrintType(IntEnum): + NONE = 0 + FromStorage = 1 + FromGasha = 2 + +class AuthType(IntEnum): + UNKNOWN = 0 + CARD = 1 + MOBLE = 2 + +class ActTypeConsumeTicket(IntEnum): + QuestEpisodeBeginner = 1 + QuestEpisode = 2 + QuestTrialTowerBeginner = 3 + QuestTrialTower = 4 + QuestPvEBeginner = 5 + QuestPvE = 6 + QuestContinue = 7 + QuesRarityUp = 8 + QuestEpisodeBoost = 9 + QuestTrialTowerBoost = 10 + QuestPvEBoost = 11 + CustomModeExtend = 21 + CustomModeRetry = 22 + YuiMedalShop = 31 + +class ActTypeChangeParty(IntEnum): + MANUAL = 1 + AUTO = 2 + +class ActTypeConsumeCredit(IntEnum): + PurchaseTicketA1 = 1 + PurchaseTicketA2 = 2 + PurchaseTicketA6 = 3 + PurchaseTicketB1 = 4 + PurchaseTicketB2 = 5 + PurchaseTicketB7 = 6 + PurchaseTicketC1 = 7 + PurchaseTicketC3 = 8 + PurchaseTicketC8 = 9 + PurchaseTicketD1 = 10 + PurchaseTicketD2 = 11 + PurchaseTicketD6 = 12 + PurchaseTicketE1 = 13 + PurchaseTicketE3 = 14 + PurchaseTicketE8 = 15 + PurchaseTicketF1 = 16 + PurchaseTicketF2 = 17 + PurchaseTicketF6 = 18 + QuestContinue = 21 + QuestRarityUp = 22 + GashaSatelite = 31 + GashaSateliteAdd1 = 32 + GashaSateliteAdd2 = 33 + GashaTerminal = 41 + GashaTerminalAdd1 = 42 + GashaTerminalAdd2 = 43 + ResourceDischarge = 44 + ResourceHolo = 45 + ProfileCardDischarge = 46 + ProfileCardHolo = 47 + +class ProtocolErrorNum(IntEnum): # header error_num field + SUCCESS = 0 + + ALREADY_LOGIN_DIFF = 1301 + ALREADY_LOGIN_SAME = 1302 + INVALID_AUTH_TYPE = 1303 + INVALID_CABINET_TYPE = 1304 + AMID_SERVER_CONNECT_ERROR = 1305 + AMID_INFO_REQUEST_ERROR = 1306 + NOT_EXIST_PLAY_DATA = 1307 + HAVE_NEVER_PLAYED_SATELLITE = 1308 + + RESOURCE_CARD_ERR1 = 4831 # ScanQRQuestProfileCard + RESOURCE_CARD_NOT_EXIST = 4832 + RESOURCE_CARD_ERR3 = 4833 # ScanQRQuestResourceCard / ScanQRShopResourceCard + RESOURCE_CARD_ERR6 = 4836 # ScanQRQuestResourceCard / ScanQRShopResourceCard + RESOURCE_CARD_ERR7 = 4837 # ScanQRQuestResourceCard / ScanQRShopResourceCard + RESOURCE_CARD_ERR8 = 4838 # ScanQRShopResourceCard + RESOURCE_CARD_ERR9 = 4839 # ScanQRQuestProfileCard + + CREDIT_GASHA_ERROR = 7111 + + PURCHASE_ERROR = 7711 + + PAYING_PLAY_END_ERROR = 9120 + + CODE_ANALYSIS_API_NOT_RESPONSE = 9201 + CODE_ANALYSIS_API_INIQUITY_SERIAL_CODE = 9202 + CODE_USE_API_USED_SERIAL_CODE = 9203 + CODE_USE_API_NOT_RESPONSE = 9206 + CODE_USE_API_NG = 9207 + CODE_USE_API_LOCK = 9208 + CODE_USE_API_MAINTENANCE = 9209 + CODE_ANALYSIS_API_NOT_MASTER_DATA = 9210 + CODE_ANALYSIS_API_EXPIRED_SERIAL_CODE = 9211 + +class ProtocolResult(IntEnum): # result field, if used + NONE = -1 + FAILED = 0 + SUCCESS = 1 + PARAM_ERROR = 2 + MAINTENANCE = 3 + CONNECT_ERROR = 99 + class BaseHelper: def __init__(self, data: bytes, offset: int) -> None: self._sz = 0 @@ -143,9 +291,10 @@ class BaseHelper: def decode_arr_cls(data: bytes, offset: int, cls: BaseHelper): size = 0 num_cls = decode_int(data, offset + size) + size += INT_OFF cls_type = type(cls) - ret: List[cls_type] = [] + ret: List[cls_type] = [] # type: ignore for _ in range(num_cls): tmp = cls(data, offset + size) size += tmp.get_size() @@ -171,65 +320,1303 @@ class MaterialCommonRewardUserData(BaseHelper): self.user_common_reward_id = user_common_reward_id[0] self._sz += user_common_reward_id[1] +class QuestScenePlayEndUnanalyzedLogRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.unanalyzed_log_grade_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_data, new_off = decode_arr_cls(data, off, CommonRewardData) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, grade: int, reward: Dict) -> "QuestScenePlayEndUnanalyzedLogRewardData": + ret = cls(b"\x00" * 8, 0) + ret.unanalyzed_log_grade_id = grade + ret.common_reward_data = [CommonRewardData.from_args(reward.get('CommonRewardType'), reward.get('CommonRewardId'), reward.get('CommonRewardNum'))] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.unanalyzed_log_grade_id) \ + + encode_arr_cls(self.common_reward_data) + class PartyTeamData(BaseHelper): - def __init__(self, data: bytes, offset: int) -> None: - sz = 0 - user_party_team_id = decode_str(data, offset + sz) - self.user_party_team_id = user_party_team_id[0] - sz += user_party_team_id[1] + def __init__(self, data: bytes, offset: int): + off = offset + self.user_party_team_id, new_off = decode_str(data, off) + off += new_off - self.arrangement_num = decode_byte(data, offset + sz) - sz += BYTE_OFF + self.arrangement_num = decode_byte(data, off) + off += BYTE_OFF - user_hero_log_id = decode_str(data, offset + sz) - self.user_hero_log_id = user_hero_log_id[0] - sz += user_hero_log_id[1] + self.user_hero_log_id, new_off = decode_str(data, off) + off += new_off - main_weapon_user_equipment_id = decode_str(data, offset + sz) - self.main_weapon_user_equipment_id = main_weapon_user_equipment_id[0] - sz += main_weapon_user_equipment_id[1] + self.main_weapon_user_equipment_id, new_off = decode_str(data, off) + off += new_off - sub_equipment_user_equipment_id = decode_str(data, offset + sz) - self.sub_equipment_user_equipment_id = sub_equipment_user_equipment_id[0] - sz += sub_equipment_user_equipment_id[1] + self.sub_equipment_user_equipment_id, new_off = decode_str(data, off) + off += new_off - self.skill_slot1_skill_id = decode_int(data, offset + sz) - sz += INT_OFF + self.skill_slot1_skill_id = decode_int(data, off) + off += INT_OFF - self.skill_slot2_skill_id = decode_int(data, offset + sz) - sz += INT_OFF + self.skill_slot2_skill_id = decode_int(data, off) + off += INT_OFF - self.skill_slot3_skill_id = decode_int(data, offset + sz) - sz += INT_OFF + self.skill_slot3_skill_id = decode_int(data, off) + off += INT_OFF - self.skill_slot4_skill_id = decode_int(data, offset + sz) - sz += INT_OFF + self.skill_slot4_skill_id = decode_int(data, off) + off += INT_OFF - self.skill_slot5_skill_id = decode_int(data, offset + sz) - sz += INT_OFF + self.skill_slot5_skill_id = decode_int(data, off) + off += INT_OFF - self._sz = sz + self._sz = off - offset + + @classmethod + def from_args(cls, party_team_id: str, arr_num: int, data: Dict) -> "PartyTeamData": + ret = cls(b"\x00" * 37, 0) + ret.user_party_team_id = party_team_id + ret.arrangement_num = arr_num + ret.user_hero_log_id = data['id'] + ret.main_weapon_user_equipment_id = data['main_weapon'] + ret.sub_equipment_user_equipment_id = data['sub_equipment'] + ret.skill_slot1_skill_id = data['skill_slot1_skill_id'] + ret.skill_slot2_skill_id = data['skill_slot2_skill_id'] + ret.skill_slot3_skill_id = data['skill_slot3_skill_id'] + ret.skill_slot4_skill_id = data['skill_slot4_skill_id'] + ret.skill_slot5_skill_id = data['skill_slot5_skill_id'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_party_team_id) \ + + encode_byte(self.arrangement_num) \ + + encode_str(self.user_hero_log_id) \ + + encode_str(self.main_weapon_user_equipment_id) \ + + encode_str(self.sub_equipment_user_equipment_id) \ + + encode_int(self.skill_slot1_skill_id) \ + + encode_int(self.skill_slot2_skill_id) \ + + encode_int(self.skill_slot3_skill_id) \ + + encode_int(self.skill_slot4_skill_id) \ + + encode_int(self.skill_slot5_skill_id) + +class CommonRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.common_reward_type = decode_short(data, off) + off += SHORT_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, reward_type: int, reward_id: int, reward_num: int) -> "CommonRewardData": + ret = cls(b"\x00" * 10, 0) + ret.common_reward_type = reward_type + ret.common_reward_id = reward_id + ret.common_reward_num = reward_num + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_int(self.common_reward_num) + +class ReadProfileCardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.profile_card_code, new_off = decode_str(data, off) + off += new_off + + self.nick_name, new_off = decode_str(data, off) + off += new_off + + self.rank_num = decode_short(data, off) + off += SHORT_OFF + + self.setting_title_id = decode_int(data, off) + off += INT_OFF + + self.skill_id = decode_short(data, off) + off += SHORT_OFF + + self.hero_log_hero_log_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_log_level = decode_short(data, off) + off += SHORT_OFF + + self.hero_log_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.hero_log_property1_property_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_property1_value1 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property1_value2 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property2_property_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_property2_value1 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property2_value2 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property3_property_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_property3_value1 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property3_value2 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property4_property_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_property4_value1 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property4_value2 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_equipment_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_enhancement_value = decode_short(data, off) + off += SHORT_OFF + + self.main_weapon_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.main_weapon_property1_property_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property1_value1 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property1_value2 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property2_property_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property2_value1 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property2_value2 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property3_property_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property3_value1 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property3_value2 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property4_property_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property4_value1 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property4_value2 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_equipment_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_enhancement_value = decode_short(data, off) + off += SHORT_OFF + + self.sub_equipment_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.sub_equipment_property1_property_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property1_value1 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property1_value2 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property2_property_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property2_value1 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property2_value2 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property3_property_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property3_value1 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property3_value2 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property4_property_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property4_value1 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property4_value2 = decode_int(data, off) + off += INT_OFF + + self.holographic_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, user_profile: Dict, hero_data: Dict) -> "ReadProfileCardData": + ret = cls(b"\x00" * 185, 0) + # TODO: real data + ret.profile_card_code = "" + ret.nick_name = "" + ret.rank_num = 0 + ret.setting_title_id = 0 + ret.skill_id = 0 + ret.hero_log_hero_log_id = 0 + ret.hero_log_log_level = 0 + ret.hero_log_awakening_stage = 0 + ret.hero_log_property1_property_id = 0 + ret.hero_log_property1_value1 = 0 + ret.hero_log_property1_value2 = 0 + ret.hero_log_property2_property_id = 0 + ret.hero_log_property2_value1 = 0 + ret.hero_log_property2_value2 = 0 + ret.hero_log_property3_property_id = 0 + ret.hero_log_property3_value1 = 0 + ret.hero_log_property3_value2 = 0 + ret.hero_log_property4_property_id = 0 + ret.hero_log_property4_value1 = 0 + ret.hero_log_property4_value2 = 0 + ret.main_weapon_equipment_id = 0 + ret.main_weapon_enhancement_value = 0 + ret.main_weapon_awakening_stage = 0 + ret.main_weapon_property1_property_id = 0 + ret.main_weapon_property1_value1 = 0 + ret.main_weapon_property1_value2 = 0 + ret.main_weapon_property2_property_id = 0 + ret.main_weapon_property2_value1 = 0 + ret.main_weapon_property2_value2 = 0 + ret.main_weapon_property3_property_id = 0 + ret.main_weapon_property3_value1 = 0 + ret.main_weapon_property3_value2 = 0 + ret.main_weapon_property4_property_id = 0 + ret.main_weapon_property4_value1 = 0 + ret.main_weapon_property4_value2 = 0 + ret.sub_equipment_equipment_id = 0 + ret.sub_equipment_enhancement_value = 0 + ret.sub_equipment_awakening_stage = 0 + ret.sub_equipment_property1_property_id = 0 + ret.sub_equipment_property1_value1 = 0 + ret.sub_equipment_property1_value2 = 0 + ret.sub_equipment_property2_property_id = 0 + ret.sub_equipment_property2_value1 = 0 + ret.sub_equipment_property2_value2 = 0 + ret.sub_equipment_property3_property_id = 0 + ret.sub_equipment_property3_value1 = 0 + ret.sub_equipment_property3_value2 = 0 + ret.sub_equipment_property4_property_id = 0 + ret.sub_equipment_property4_value1 = 0 + ret.sub_equipment_property4_value2 = 0 + ret.holographic_flag = 0 + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.profile_card_code) \ + + encode_str(self.nick_name) \ + + encode_short(self.rank_num) \ + + encode_int(self.setting_title_id) \ + + encode_short(self.skill_id) \ + + encode_int(self.hero_log_hero_log_id) \ + + encode_short(self.hero_log_log_level) \ + + encode_short(self.hero_log_awakening_stage) \ + + encode_int(self.hero_log_property1_property_id) \ + + encode_int(self.hero_log_property1_value1) \ + + encode_int(self.hero_log_property1_value2) \ + + encode_int(self.hero_log_property2_property_id) \ + + encode_int(self.hero_log_property2_value1) \ + + encode_int(self.hero_log_property2_value2) \ + + encode_int(self.hero_log_property3_property_id) \ + + encode_int(self.hero_log_property3_value1) \ + + encode_int(self.hero_log_property3_value2) \ + + encode_int(self.hero_log_property4_property_id) \ + + encode_int(self.hero_log_property4_value1) \ + + encode_int(self.hero_log_property4_value2) \ + + encode_int(self.main_weapon_equipment_id) \ + + encode_short(self.main_weapon_enhancement_value) \ + + encode_short(self.main_weapon_awakening_stage) \ + + encode_int(self.main_weapon_property1_property_id) \ + + encode_int(self.main_weapon_property1_value1) \ + + encode_int(self.main_weapon_property1_value2) \ + + encode_int(self.main_weapon_property2_property_id) \ + + encode_int(self.main_weapon_property2_value1) \ + + encode_int(self.main_weapon_property2_value2) \ + + encode_int(self.main_weapon_property3_property_id) \ + + encode_int(self.main_weapon_property3_value1) \ + + encode_int(self.main_weapon_property3_value2) \ + + encode_int(self.main_weapon_property4_property_id) \ + + encode_int(self.main_weapon_property4_value1) \ + + encode_int(self.main_weapon_property4_value2) \ + + encode_int(self.sub_equipment_equipment_id) \ + + encode_short(self.sub_equipment_enhancement_value) \ + + encode_short(self.sub_equipment_awakening_stage) \ + + encode_int(self.sub_equipment_property1_property_id) \ + + encode_int(self.sub_equipment_property1_value1) \ + + encode_int(self.sub_equipment_property1_value2) \ + + encode_int(self.sub_equipment_property2_property_id) \ + + encode_int(self.sub_equipment_property2_value1) \ + + encode_int(self.sub_equipment_property2_value2) \ + + encode_int(self.sub_equipment_property3_property_id) \ + + encode_int(self.sub_equipment_property3_value1) \ + + encode_int(self.sub_equipment_property3_value2) \ + + encode_int(self.sub_equipment_property4_property_id) \ + + encode_int(self.sub_equipment_property4_value1) \ + + encode_int(self.sub_equipment_property4_value2) \ + + encode_byte(self.holographic_flag) + +class QuestSceneBestScoreUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.clear_time = decode_int(data, off) + off += INT_OFF + + self.combo_num = decode_int(data, off) + off += INT_OFF + + self.total_damage, new_off = decode_str(data, off) + off += new_off + + self.concurrent_destroying_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestSceneBestScoreUserData": + ret = cls(b"\x00" * 14, 0) + ret.clear_time = data['clear_time'] + ret.combo_num = data['combo_num'] + ret.total_damage = data['total_damage'] + ret.concurrent_destroying_num = data['concurrent_destroying_num'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.clear_time) \ + + encode_int(self.combo_num) \ + + encode_str(self.total_damage) \ + + encode_short(self.concurrent_destroying_num) + +class QuestSceneExBonusUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ex_bonus_table_id = decode_int(data, off) + off += INT_OFF + + self.achievement_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, table_id: int = 0, ach_flag: bool = False) -> "QuestSceneExBonusUserData": + ret = cls(b"\x00" * 5, 0) + ret.ex_bonus_table_id = table_id + ret.achievement_flag = ach_flag + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.ex_bonus_table_id) \ + + encode_byte(self.achievement_flag) + +class QuestSceneUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_type = decode_byte(data, off) + off += BYTE_OFF + + self.quest_scene_id = decode_short(data, off) + off += SHORT_OFF + + self.clear_flag = decode_byte(data, off) + off += BYTE_OFF + + self.quest_scene_best_score_user_data: List[QuestSceneBestScoreUserData] = [] + self.quest_scene_best_score_user_data, new_off = decode_arr_cls(data, off, QuestSceneBestScoreUserData) + off += new_off + + self.quest_scene_ex_bonus_user_data_list: List[QuestSceneExBonusUserData] = [] + self.quest_scene_ex_bonus_user_data_list, new_off = decode_arr_cls(data, off, QuestSceneExBonusUserData) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestSceneUserData": + ret = cls(b"\x00" * 12, 0) + ret.quest_type = data['quest_type'] + ret.quest_scene_id = data['quest_scene_id'] + ret.clear_flag = data['quest_clear_flag'] + ret.quest_scene_best_score_user_data = [QuestSceneBestScoreUserData.from_args(data)] + ret.quest_scene_ex_bonus_user_data_list = [] # TODO + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.quest_type) \ + + encode_short(self.quest_scene_id) \ + + encode_byte(self.clear_flag) \ + + encode_arr_cls(self.quest_scene_best_score_user_data) \ + + encode_arr_cls(self.quest_scene_ex_bonus_user_data_list) + +class QuestScenePlayStartAppearancePlayerTraceData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.user_quest_scene_player_trace_id, new_off = decode_str(data, off) + off += new_off + + self.nick_name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, sesh_id:int, nickname: str) -> "QuestScenePlayStartAppearancePlayerTraceData": + ret = cls(b"\x00" * 8, 0) + ret.user_quest_scene_player_trace_id = str(sesh_id) + ret.nick_name = nickname + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_quest_scene_player_trace_id) \ + + encode_str(self.nick_name) + +class QuestScenePlayStartResponseData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.appearance_player_trace_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayStartAppearancePlayerTraceData) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, sesh_id: int, nickname: str) -> "QuestScenePlayStartResponseData": + ret = cls(b"\x00" * 99, 0) + ret.appearance_player_trace_data_list = [QuestScenePlayStartAppearancePlayerTraceData.from_args(sesh_id, nickname)] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_arr_cls(self.appearance_player_trace_data_list) + +class QuestScenePlayStartRequestData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.user_party_id, new_off = decode_str(data, off) + off += new_off + + self.appoint_leader_resource_card_code, new_off = decode_str(data, off) + off += new_off + + self.use_profile_card_code, new_off = decode_str(data, off) + off += new_off + + self.quest_drop_boost_apply_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayStartRequestData": + ret = cls(b"\x00" * 13, 0) + ret.user_party_id = data['UserPartyId'] + ret.appoint_leader_resource_card_code = data['AppointLeaderResourceCardCode'] + ret.use_profile_card_code = data['UseProfileCardCode'] + ret.quest_drop_boost_apply_flag = data['QuestDropBoostApplyFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_party_id) \ + + encode_str(self.appoint_leader_resource_card_code) \ + + encode_str(self.use_profile_card_code) \ + + encode_byte(self.quest_drop_boost_apply_flag) + +class QuestSceneMultiPlayStartEntryUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.host_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestSceneMultiPlayStartEntryUserData": + ret = cls(b"\x00" * 9, 0) + ret.store_id = data['StoreId'] + ret.user_id = data['UserId'] + ret.host_flag = data['HostFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.store_id) \ + + encode_str(self.user_id) \ + + encode_byte(self.host_flag) + +class QuestSceneMultiPlayStartRequestData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.room_id, new_off = decode_str(data, off) + off += new_off + + self.matching_mode = decode_byte(data, off) + off += BYTE_OFF + + self.entry_user_data_list: List[QuestSceneMultiPlayStartEntryUserData] = [] + self.entry_user_data_list, new_off = decode_arr_cls(data, off, QuestSceneMultiPlayStartEntryUserData) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestSceneMultiPlayStartRequestData": + ret = cls(b"\x00" * 9, 0) + ret.room_id = data['RoomId'] + ret.matching_mode = data['MatchingMode'] + ret.entry_user_data_list = [] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.room_id) \ + + encode_byte(self.matching_mode) \ + + encode_arr_cls(self.entry_user_data_list) + +class QuestSceneMultiPlayStartResponseData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.dummy_1 = decode_byte(data, off) + off += BYTE_OFF + + self.dummy_2 = decode_byte(data, off) + off += BYTE_OFF + + self.dummy_3 = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls) -> "QuestSceneMultiPlayStartResponseData": + ret = cls(b"\x00" * 3, 0) + ret.dummy_1 = 0 + ret.dummy_2 = 0 + ret.dummy_3 = 0 + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.dummy_1) \ + + encode_byte(self.dummy_2) \ + + encode_byte(self.dummy_3) + +class QuestSceneMultiPlayEndResponseData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.dummy_1 = decode_byte(data, off) + off += BYTE_OFF + + self.dummy_2 = decode_byte(data, off) + off += BYTE_OFF + + self.dummy_3 = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls) -> "QuestSceneMultiPlayEndResponseData": + ret = cls(b"\x00" * 3, 0) + ret.dummy_1 = 0 + ret.dummy_2 = 0 + ret.dummy_3 = 0 + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.dummy_1) \ + + encode_byte(self.dummy_2) \ + + encode_byte(self.dummy_3) + +class QuestScenePlayEndBaseGetData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.get_hero_log_exp = decode_int(data, off) + off += INT_OFF + + self.get_col = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndBaseGetData": + ret = cls(b"\x00" * 99, 0) + ret.get_hero_log_exp = data['GetHeroLogExp'] + ret.get_col = data['GetCol'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.get_hero_log_exp) \ + + encode_int(self.get_col) + +class QuestScenePlayEndDiscoveryEnemyData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.enemy_kind_id = decode_int(data, off) + off += INT_OFF + + self.destroy_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndDiscoveryEnemyData": + ret = cls(b"\x00" * 99, 0) + ret.enemy_kind_id = data['EnemyKindId'] + ret.destroy_num = data['DestroyNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.enemy_kind_id) \ + + encode_short(self.destroy_num) + +class QuestScenePlayEndGetPlayerTraceData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.user_quest_scene_player_trace_id, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndGetPlayerTraceData": + ret = cls(b"\x00" * 99, 0) + ret.user_quest_scene_player_trace_id = data['UserQuestScenePlayerTraceId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_quest_scene_player_trace_id) + +class QuestScenePlayEndGetRareDropData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_rare_drop_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndGetRareDropData": + ret = cls(b"\x00" * 99, 0) + ret.quest_rare_drop_id = data['QuestRareDropId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.quest_rare_drop_id) + +class QuestScenePlayEndGetSpecialRareDropData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_special_rare_drop_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndGetSpecialRareDropData": + ret = cls(b"\x00" * 99, 0) + ret.quest_special_rare_drop_id = data['QuestSpecialRareDropId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.quest_special_rare_drop_id) + +class QuestScenePlayEndGetUnanalyzedLogTmpRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.unanalyzed_log_grade_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndGetUnanalyzedLogTmpRewardData": + ret = cls(b"\x00" * 99, 0) + ret.unanalyzed_log_grade_id = data['UnanalyzedLogGradeId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.unanalyzed_log_grade_id) + +class QuestScenePlayEndScoreData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.clear_time = decode_int(data, off) + off += INT_OFF + + self.combo_num = decode_int(data, off) + off += INT_OFF + + self.total_damage, new_off = decode_str(data, off) + off += new_off + + self.concurrent_destroying_num = decode_short(data, off) + off += SHORT_OFF + + self.reaching_skill_level = decode_short(data, off) + off += SHORT_OFF + + self.ko_chara_num = decode_byte(data, off) + off += BYTE_OFF + + self.acceleration_invocation_num = decode_short(data, off) + off += SHORT_OFF + + self.boss_destroying_num = decode_short(data, off) + off += SHORT_OFF + + self.synchro_skill_used_flag = decode_byte(data, off) + off += BYTE_OFF + + self.used_friend_skill_id = decode_int(data, off) + off += INT_OFF + + self.friend_skill_used_flag = decode_byte(data, off) + off += BYTE_OFF + + self.continue_cnt = decode_short(data, off) + off += SHORT_OFF + + self.total_loss_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndScoreData": + ret = cls(b"\x00" * 99, 0) + ret.clear_time = data['ClearTime'] + ret.combo_num = data['ComboNum'] + ret.total_damage = data['TotalDamage'] + ret.concurrent_destroying_num = data['ConcurrentDestroyingNum'] + ret.reaching_skill_level = data['ReachingSkillLevel'] + ret.ko_chara_num = data['KoCharaNum'] + ret.acceleration_invocation_num = data['AccelerationInvocationNum'] + ret.boss_destroying_num = data['BossDestroyingNum'] + ret.synchro_skill_used_flag = data['SynchroSkillUsedFlag'] + ret.used_friend_skill_id = data['UsedFriendSkillId'] + ret.friend_skill_used_flag = data['FriendSkillUsedFlag'] + ret.continue_cnt = data['ContinueCnt'] + ret.total_loss_num = data['TotalLossNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.clear_time) \ + + encode_int(self.combo_num) \ + + encode_str(self.total_damage) \ + + encode_short(self.concurrent_destroying_num) \ + + encode_short(self.reaching_skill_level) \ + + encode_byte(self.ko_chara_num) \ + + encode_short(self.acceleration_invocation_num) \ + + encode_short(self.boss_destroying_num) \ + + encode_byte(self.synchro_skill_used_flag) \ + + encode_int(self.used_friend_skill_id) \ + + encode_byte(self.friend_skill_used_flag) \ + + encode_short(self.continue_cnt) \ + + encode_short(self.total_loss_num) + +class QuestScenePlayEndMissionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.mission_id = decode_int(data, off) + off += INT_OFF + + self.clear_flag = decode_byte(data, off) + off += BYTE_OFF + + self.mission_difficulty_id = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndMissionData": + ret = cls(b"\x00" * 99, 0) + ret.mission_id = data['MissionId'] + ret.clear_flag = data['ClearFlag'] + ret.mission_difficulty_id = data['MissionDifficultyId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.mission_id) \ + + encode_byte(self.clear_flag) \ + + encode_short(self.mission_difficulty_id) + +class QuestScenePlayEndDestroyBossData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.boss_type = decode_byte(data, off) + off += BYTE_OFF + + self.enemy_kind_id = decode_int(data, off) + off += INT_OFF + + self.destroy_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndDestroyBossData": + ret = cls(b"\x00" * 99, 0) + ret.boss_type = data['BossType'] + ret.enemy_kind_id = data['EnemyKindId'] + ret.destroy_num = data['DestroyNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.boss_type) \ + + encode_int(self.enemy_kind_id) \ + + encode_short(self.destroy_num) + +class QuestScenePlayEndGetEventItemData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.event_item_id = decode_int(data, off) + off += INT_OFF + + self.get_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndGetEventItemData": + ret = cls(b"\x00" * 99, 0) + ret.event_item_id = data['EventItemId'] + ret.get_num = data['GetNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.event_item_id) \ + + encode_short(self.get_num) + +class QuestTreasureHuntPlayEndResponseData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.get_event_point = decode_int(data, off) + off += INT_OFF + + self.total_event_point = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, get_pt: int = 0, total_pt: int = 0) -> "QuestTreasureHuntPlayEndResponseData": + ret = cls(b"\x00" * 8, 0) + ret.get_event_point = get_pt + ret.total_event_point = total_pt + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.get_event_point) \ + + encode_int(self.total_event_point) + +class QuestTrialTowerPlayEndUpdatedNotificationData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.store_best_score_clear_time_flag = decode_byte(data, off) + off += BYTE_OFF + + self.store_best_score_combo_num_flag = decode_byte(data, off) + off += BYTE_OFF + + self.store_best_score_total_damage_flag = decode_byte(data, off) + off += BYTE_OFF + + self.store_best_score_concurrent_destroying_num_flag = decode_byte(data, off) + off += BYTE_OFF + + self.store_reaching_trial_tower_rank = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, shop_best_time: bool = False, shop_best_combo: bool = False, shop_best_damage: bool = False, shop_best_concurrent: bool = False, shop_tower_rank: int = 0) -> "QuestTrialTowerPlayEndUpdatedNotificationData": + ret = cls(b"\x00" * 8, 0) + ret.store_best_score_clear_time_flag = shop_best_time + ret.store_best_score_combo_num_flag = shop_best_combo + ret.store_best_score_total_damage_flag = shop_best_damage + ret.store_best_score_concurrent_destroying_num_flag = shop_best_concurrent + ret.store_reaching_trial_tower_rank = shop_tower_rank + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.store_best_score_clear_time_flag) \ + + encode_byte(self.store_best_score_combo_num_flag) \ + + encode_byte(self.store_best_score_total_damage_flag) \ + + encode_byte(self.store_best_score_concurrent_destroying_num_flag) \ + + encode_int(self.store_reaching_trial_tower_rank) + +class CommonRewardUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.common_reward_type = decode_short(data, off) + off += SHORT_OFF + + self.user_common_reward_id, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "CommonRewardUserData": + ret = cls(b"\x00" * 99, 0) + ret.common_reward_type = data['CommonRewardType'] + ret.user_common_reward_id = data['UserCommonRewardId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.common_reward_type) \ + + encode_str(self.user_common_reward_id) + +class QuestScenePlayEndResponseData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.rarity_up_occurrence_flag = decode_byte(data, off) + off += BYTE_OFF + + self.adventure_ex_area_occurrences_flag = decode_byte(data, off) + off += BYTE_OFF + + self.ex_bonus_data_list: List[QuestScenePlayEndExBonusData] = [] + self.ex_bonus_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndExBonusData) + off += new_off + + self.play_end_player_trace_reward_data_list: List[QuestScenePlayEndPlayerTraceRewardData] = [] + self.play_end_player_trace_reward_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndPlayerTraceRewardData) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, rarity_up_flag: bool = False, adventure_ex_area_flg: bool = False, ex_tables: List = []) -> "QuestScenePlayEndResponseData": + ret = cls(b"\x00" * 99, 0) + ret.rarity_up_occurrence_flag = rarity_up_flag + ret.adventure_ex_area_occurrences_flag = adventure_ex_area_flg + for x in ex_tables: + ret.ex_bonus_data_list.append(QuestScenePlayEndExBonusData.from_args(x['table'], x['ach_status'])) + ret.play_end_player_trace_reward_data_list = [] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.rarity_up_occurrence_flag) \ + + encode_byte(self.adventure_ex_area_occurrences_flag) \ + + encode_arr_cls(self.ex_bonus_data_list) \ + + encode_arr_cls(self.play_end_player_trace_reward_data_list) + +class QuestScenePlayEndExBonusData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ex_bonus_table_id = decode_int(data, off) + off += INT_OFF + + self.achievement_status = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, table_id: int, ach_status: int) -> "QuestScenePlayEndExBonusData": + ret = cls(b"\x00" * 5, 0) + ret.ex_bonus_table_id = table_id + ret.achievement_status = ach_status + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.ex_bonus_table_id) \ + + encode_byte(self.achievement_status) + +class QuestScenePlayEndPlayerTraceRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.common_reward_data: List[CommonRewardData] + self.common_reward_data, new_off = decode_arr_cls(data, off, CommonRewardData) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, reward_data: Dict) -> "QuestScenePlayEndPlayerTraceRewardData": + ret = cls(b"\x00" * 4, 0) + ret.common_reward_data.append(CommonRewardData.from_args(reward_data['CommonRewardType'], reward_data['CommonRewardId'], reward_data['CommonRewardNum'])) + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_arr_cls(self.common_reward_data) + +class QuestScenePlayEndRequestData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.play_result_flag = decode_byte(data, off) + off += BYTE_OFF + + self.base_get_data: List[QuestScenePlayEndBaseGetData] = [] + self.base_get_data, new_off = decode_arr_cls(data, off, QuestScenePlayEndBaseGetData) + off += new_off + + self.get_player_trace_data_list: List[QuestScenePlayEndGetPlayerTraceData] = [] + self.get_player_trace_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndGetPlayerTraceData) + off += new_off + + self.get_rare_drop_data_list: List[QuestScenePlayEndGetRareDropData] = [] + self.get_rare_drop_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndGetRareDropData) + off += new_off + + self.get_special_rare_drop_data_list: List[QuestScenePlayEndGetSpecialRareDropData] = [] + self.get_special_rare_drop_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndGetSpecialRareDropData) + off += new_off + + self.get_unanalyzed_log_tmp_reward_data_list: List[QuestScenePlayEndGetUnanalyzedLogTmpRewardData] = [] + self.get_unanalyzed_log_tmp_reward_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndGetUnanalyzedLogTmpRewardData) + off += new_off + + self.get_event_item_data_list: List[QuestScenePlayEndGetEventItemData] = [] + self.get_event_item_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndGetEventItemData) + off += new_off + + self.discovery_enemy_data_list: List[QuestScenePlayEndDiscoveryEnemyData] = [] + self.discovery_enemy_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndDiscoveryEnemyData) + off += new_off + + self.destroy_boss_data_list: List[QuestScenePlayEndDestroyBossData] = [] + self.destroy_boss_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndDestroyBossData) + off += new_off + + self.mission_data_list: List[QuestScenePlayEndMissionData] = [] + self.mission_data_list, new_off = decode_arr_cls(data, off, QuestScenePlayEndMissionData) + off += new_off + + self.score_data: List[QuestScenePlayEndScoreData] = [] + self.score_data, new_off = decode_arr_cls(data, off, QuestScenePlayEndScoreData) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestScenePlayEndRequestData": + ret = cls(b"\x00" * 99, 0) + ret.play_result_flag = data['PlayResultFlag'] + ret.base_get_data = [] + ret.get_player_trace_data_list = [] + ret.get_rare_drop_data_list = [] + ret.get_special_rare_drop_data_list = [] + ret.get_unanalyzed_log_tmp_reward_data_list = [] + ret.get_event_item_data_list = [] + ret.discovery_enemy_data_list = [] + ret.destroy_boss_data_list = [] + ret.mission_data_list = [] + ret.score_data = [] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.play_result_flag) \ + + encode_arr_cls(self.base_get_data) \ + + encode_arr_cls(self.get_player_trace_data_list) \ + + encode_arr_cls(self.get_rare_drop_data_list) \ + + encode_arr_cls(self.get_special_rare_drop_data_list) \ + + encode_arr_cls(self.get_unanalyzed_log_tmp_reward_data_list) \ + + encode_arr_cls(self.get_event_item_data_list) \ + + encode_arr_cls(self.discovery_enemy_data_list) \ + + encode_arr_cls(self.destroy_boss_data_list) \ + + encode_arr_cls(self.mission_data_list) \ + + encode_arr_cls(self.score_data) + +class QuestSceneMultiPlayEndRequestData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.dummy_1 = decode_byte(data, off) + off += BYTE_OFF + + self.dummy_2 = decode_byte(data, off) + off += BYTE_OFF + + self.dummy_3 = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls) -> "QuestSceneMultiPlayEndRequestData": + ret = cls(b"\x00" * 3, 0) + ret.dummy_1 = 0 + ret.dummy_2 = 0 + ret.dummy_3 = 0 + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.dummy_1) \ + + encode_byte(self.dummy_2) \ + + encode_byte(self.dummy_3) + +class ChatSideStoryUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.chat_side_story_id = decode_int(data, off) + off += INT_OFF + + self.played_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ChatSideStoryUserData": + ret = cls(b"\x00" * 99, 0) + ret.chat_side_story_id = data['ChatSideStoryId'] + ret.played_flag = data['PlayedFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.chat_side_story_id) \ + + encode_byte(self.played_flag) class PartyData(BaseHelper): - def __init__(self, data: bytes, offset: int) -> None: - sz = 0 - user_party_id = decode_str(data, offset + sz) - self.user_party_id = user_party_id[0] - sz += user_party_id[1] + def __init__(self, data: bytes, offset: int): + off = offset + self.user_party_id, new_off = decode_str(data, off) + off += new_off - self.team_no = decode_byte(data, offset + sz) - sz += BYTE_OFF - - self.party_team_data_count = decode_int(data, offset + sz) - sz += INT_OFF + self.team_no = decode_byte(data, off) + off += BYTE_OFF self.party_team_data_list: List[PartyTeamData] = [] - for _ in range(self.party_team_data_count): - tmp = PartyTeamData(data, offset + sz) - self.party_team_data_list.append(tmp) - sz += tmp.get_size() + self.party_team_data_list, new_off = decode_arr_cls(data, off, PartyTeamData) + off += new_off - self._sz = sz + self._sz = off - offset + + @classmethod + def from_args(cls, user_party_id: int, team_num: int, member1: Dict, member2: Dict, member3: Dict) -> "PartyData": + ret = cls(b"\x00" * 9, 0) + ret.user_party_id = user_party_id + ret.team_no = team_num + ret.party_team_data_list = [ + PartyTeamData.from_args(f"{user_party_id}-{team_num}-1", 1, member1), + PartyTeamData.from_args(f"{user_party_id}-{team_num}-2", 2, member2), + PartyTeamData.from_args(f"{user_party_id}-{team_num}-3", 3, member3), + ] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_party_id) \ + + encode_byte(self.team_no) \ + + encode_arr_cls(self.party_team_data_list) + +class SupportLogPartyData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.party_no = decode_byte(data, off) + off += BYTE_OFF + + self.arrangement_num_1_user_support_log_id, new_off = decode_str(data, off) + off += new_off + + self.arrangement_num_2_user_support_log_id, new_off = decode_str(data, off) + off += new_off + + self.arrangement_num_3_user_support_log_id, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SupportLogPartyData": + ret = cls(b"\x00" * 99, 0) + ret.party_no = data['PartyNo'] + ret.arrangement_num_1_user_support_log_id = data['ArrangementNum1UserSupportLogId'] + ret.arrangement_num_2_user_support_log_id = data['ArrangementNum2UserSupportLogId'] + ret.arrangement_num_3_user_support_log_id = data['ArrangementNum3UserSupportLogId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.party_no) \ + + encode_str(self.arrangement_num_1_user_support_log_id) \ + + encode_str(self.arrangement_num_2_user_support_log_id) \ + + encode_str(self.arrangement_num_3_user_support_log_id) class PlayStartRequestData(BaseHelper): def __init__(self, data: bytes, offset: int) -> None: @@ -607,7 +1994,7 @@ class ShopResourceSalesData(BaseHelper): ret += encode_str(self.discharge_user_id) ret += encode_short(self.remaining_num) ret += encode_short(self.purchase_num) - ret += encode_str(fmt_dt(self.sales_start_date)) + ret += encode_date_str(self.sales_start_date) ret += encode_arr_cls(self.sales_resource_data_list) return ret @@ -635,7 +2022,7 @@ class YuiMedalShopUserData(BaseHelper): def make(self) -> bytes: ret = encode_int(self.yui_medal_shop_id) ret += encode_int(self.purchase_num) - ret += encode_str(fmt_dt(self.last_purchase_date)) + ret += encode_date_str(self.last_purchase_date) return ret class GashaMedalShopUserData(BaseHelper): @@ -659,263 +2046,6 @@ class GashaMedalShopUserData(BaseHelper): ret += encode_int(self.purchase_num) return ret -class YuiMedalShopData(BaseHelper): - def __init__(self, data: bytes, offset: int) -> None: - super().__init__(data, offset) - self.yui_medal_shop_id = decode_int(data, offset + self._sz) - - name = decode_str(data, offset + self._sz) - self.name = name[0] - self._sz += name[1] - - description = decode_str(data, offset + self._sz) - self.description = description[0] - self._sz += description[1] - - self.selling_yui_medal = decode_short(data, offset + self._sz) - self._sz += SHORT_OFF - self.selling_col = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.selling_event_item_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.selling_event_item_num = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.selling_ticket_num = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.purchase_limit = decode_short(data, offset + self._sz) - self._sz += SHORT_OFF - self.pick_up_flag = decode_byte(data, offset + self._sz) - self._sz += BYTE_OFF - self.product_category = decode_byte(data, offset + self._sz) - self._sz += BYTE_OFF - self.sales_type = decode_byte(data, offset + self._sz) - self._sz += BYTE_OFF - self.target_days = decode_byte(data, offset + self._sz) - self._sz += BYTE_OFF - self.target_hour = decode_byte(data, offset + self._sz) - self._sz += BYTE_OFF - self.interval_hour = decode_byte(data, offset + self._sz) - self._sz += BYTE_OFF - - sales_start_date = decode_str(data, offset + self._sz) - self.sales_start_date = prs_dt(sales_start_date[0]) - self._sz += sales_start_date[1] - - sales_end_date = decode_str(data, offset + self._sz) - self.sales_end_date = prs_dt(sales_end_date[0]) - self._sz += sales_end_date[1] - - self.sort = decode_byte(data, offset + self._sz) - - @classmethod - def from_args(cls, shop_id: int = 0, name: str = "", desc: str = "") -> "YuiMedalShopData": - ret = cls(b"\x00" * 43, 0) - ret.yui_medal_shop_id = shop_id - ret.name = name - ret.description = desc - return ret - - def make(self) -> bytes: - ret = encode_int(self.yui_medal_shop_id) - ret += encode_str(self.name) - ret += encode_str(self.description) - ret += encode_short(self.selling_yui_medal) - ret += encode_int(self.selling_col) - ret += encode_int(self.selling_event_item_id) - ret += encode_int(self.selling_event_item_num) - ret += encode_int(self.selling_ticket_num) - ret += encode_short(self.purchase_limit) - ret += encode_byte(self.pick_up_flag) - ret += encode_byte(self.product_category) - ret += encode_byte(self.sales_type) - ret += encode_byte(self.target_days) - ret += encode_byte(self.target_hour) - ret += encode_byte(self.interval_hour) - ret += encode_str(fmt_dt(self.sales_start_date)) - ret += encode_str(fmt_dt(self.sales_end_date)) - ret += encode_byte(self.sort) - return ret - -class YuiMedalShopItemData(BaseHelper): - def __init__(self, data: bytes, offset: int) -> None: - super().__init__(data, offset) - self.yui_medal_shop_item_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.yui_medal_shop_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.common_reward_type = decode_byte(data, offset + self._sz) - self._sz += BYTE_OFF - self.common_reward_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.common_reward_num = decode_short(data, offset + self._sz) - self._sz += SHORT_OFF - self.strength = decode_int(data, offset + self._sz) - self._sz += INT_OFF - - self.property1_property_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.property1_value1 = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.property1_value2 = decode_int(data, offset + self._sz) - self._sz += INT_OFF - - self.property2_property_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.property2_value1 = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.property2_value2 = decode_int(data, offset + self._sz) - self._sz += INT_OFF - - self.property3_property_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.property3_value1 = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.property3_value2 = decode_int(data, offset + self._sz) - self._sz += INT_OFF - - self.property4_property_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.property4_value1 = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.property4_value2 = decode_int(data, offset + self._sz) - self._sz += INT_OFF - - @classmethod - def from_args(cls, item_id: int = 0, shop_id: int = 0, reward_type: int = 0, reward_id: int = 0, reward_num: int = 0, strength: int = 0) -> "YuiMedalShopItemData": - ret = cls(b"\x00" * 67, 0) - ret.yui_medal_shop_item_id = item_id - ret.yui_medal_shop_id = shop_id - ret.common_reward_type = reward_type - ret.common_reward_id = reward_id - ret.common_reward_num = reward_num - ret.strength = strength - return ret - - def make(self) -> bytes: - ret = encode_int(self.yui_medal_shop_item_id) - ret += encode_int(self.yui_medal_shop_id) - ret += encode_byte(self.common_reward_type) - ret += encode_int(self.common_reward_id) - ret += encode_short(self.common_reward_num) - ret += encode_int(self.strength) - - ret += encode_int(self.property1_property_id) - ret += encode_int(self.property1_value1) - ret += encode_int(self.property1_value2) - - ret += encode_int(self.property2_property_id) - ret += encode_int(self.property2_value1) - ret += encode_int(self.property2_value2) - - ret += encode_int(self.property3_property_id) - ret += encode_int(self.property3_value1) - ret += encode_int(self.property3_value2) - - ret += encode_int(self.property4_property_id) - ret += encode_int(self.property4_value1) - ret += encode_int(self.property4_value2) - return ret - -class ResEarnCampaignShop(BaseHelper): - def __init__(self, data: bytes, offset: int) -> None: - super().__init__(data, offset) - self.res_earn_campaign_shop_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.res_earn_campaign_application_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - - name = decode_str(data, offset + self._sz) - self.name = name[0] - self._sz += name[1] - - self.selling_yui_medal = decode_short(data, offset + self._sz) - self._sz += SHORT_OFF - self.selling_col = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.selling_event_item_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.selling_event_item_num = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.purchase_limit = decode_short(data, offset + self._sz) - self._sz += SHORT_OFF - self.get_application_point = decode_short(data, offset + self._sz) - self._sz += SHORT_OFF - - sales_start_date = decode_str(data, offset + self._sz) - self.sales_start_date = prs_dt(sales_start_date[0]) - self._sz += sales_start_date[1] - - sales_end_date = decode_str(data, offset + self._sz) - self.sales_end_date = prs_dt(sales_end_date[0]) - self._sz += sales_end_date[1] - - @classmethod - def from_args(cls, shop_id: int = 0, app_id: int = 0, name: str = "") -> "ResEarnCampaignShop": - ret = cls(b"\x00" * 26, 0) - ret.res_earn_campaign_shop_id = shop_id - ret.res_earn_campaign_application_id = app_id - ret.name = name - return ret - - def make(self) -> bytes: - ret = encode_int(self.res_earn_campaign_shop_id) - ret = encode_int(self.res_earn_campaign_application_id) - ret += encode_str(self.name) - ret += encode_short(self.selling_yui_medal) - ret += encode_int(self.selling_col) - ret += encode_int(self.selling_event_item_id) - ret += encode_int(self.selling_event_item_num) - ret += encode_short(self.purchase_limit) - ret += encode_short(self.get_application_point) - ret += encode_str(fmt_dt(self.sales_start_date)) - ret += encode_str(fmt_dt(self.sales_end_date)) - return ret - -class GashaMedalShop(BaseHelper): - def __init__(self, data: bytes, offset: int) -> None: - super().__init__(data, offset) - self.gasha_medal_shop_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - - name = decode_str(data, offset + self._sz) - self.name = name[0] - self._sz += name[1] - - self.gasha_medal_id = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.use_gasha_medal_num = decode_int(data, offset + self._sz) - self._sz += INT_OFF - self.purchase_limit = decode_short(data, offset + self._sz) - self._sz += SHORT_OFF - - sales_start_date = decode_str(data, offset + self._sz) - self.sales_start_date = prs_dt(sales_start_date[0]) - self._sz += sales_start_date[1] - - sales_end_date = decode_str(data, offset + self._sz) - self.sales_end_date = prs_dt(sales_end_date[0]) - self._sz += sales_end_date[1] - - @classmethod - def from_args(cls, shop_id: int = 0, name: str = "", medal_id: int = 0, medal_num: int = 0, purchase_limit: int = 0) -> "GashaMedalShop": - ret = cls(b"\x00" * 26, 0) - ret.gasha_medal_shop_id = shop_id - ret.name = name - ret.gasha_medal_id = medal_id - ret.use_gasha_medal_num = medal_num - ret.purchase_limit = purchase_limit - return ret - - def make(self) -> bytes: - ret = encode_int(self.gasha_medal_shop_id) - ret += encode_str(self.name) - ret += encode_int(self.gasha_medal_id) - ret += encode_int(self.use_gasha_medal_num) - ret += encode_short(self.purchase_limit) - ret += encode_str(fmt_dt(self.sales_start_date)) - ret += encode_str(fmt_dt(self.sales_end_date)) - return ret - class QuestHierarchyProgressDegreesRankingData(BaseHelper): def __init__(self, data: bytes, offset: int) -> None: super().__init__(data, offset) @@ -977,10 +2107,10 @@ class PopularHeroLogRankingData(BaseHelper): @classmethod def from_args(cls, ranking: int, hero_id: int, used_num: int) -> "PopularHeroLogRankingData": - ret = cls(b"\x00" * 12, 0) - cls.ranking = ranking - cls.hero_log_id = hero_id - cls.used_num = used_num + ret = cls(b"\x00" * 992, 0) + ret.rank = ranking + ret.hero_log_id = hero_id + ret.used_num = used_num return ret def make(self) -> bytes: @@ -988,3 +2118,10564 @@ class PopularHeroLogRankingData(BaseHelper): ret += encode_int(self.hero_log_id) ret += encode_int(self.used_num) return ret + +class DefragMatchBasicUserData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.seed_flag = decode_short(data, off) + off += SHORT_OFF + + self.ad_confirm_flag = decode_byte(data, off) + off += BYTE_OFF + + self.total_league_point = decode_int(data, off) + off += INT_OFF + + self.have_league_score = decode_short(data, off) + off += SHORT_OFF + + self.class_num = decode_short(data, off) + off += SHORT_OFF + + self.hall_of_fame_confirm_flag = decode_byte(data, off) + off += BYTE_OFF + self._sz = off + + @classmethod + def from_args(cls, seed_flag: int = 0, ad_confirm_flag: int = 0, total_league_point: int = 0, league_score: int = 0, class_num: int = 1, hof_flag: int = 0) -> "DefragMatchBasicUserData": + ret = cls(b"\x00" * 12, 0) + ret.seed_flag = seed_flag + ret.ad_confirm_flag = ad_confirm_flag + ret.total_league_point = total_league_point + ret.have_league_score = league_score + ret.class_num = class_num + ret.hall_of_fame_confirm_flag = hof_flag + + def make(self) -> bytes: + ret = encode_short(self.seed_flag) + ret += encode_byte(self.ad_confirm_flag) + ret += encode_int(self.total_league_point) + ret += encode_short(self.have_league_score) + ret += encode_short(self.class_num) + ret += encode_byte(self.hall_of_fame_confirm_flag) + return ret + +class DefragMatchRankingUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.league_point_rank = decode_int(data, off) + off += INT_OFF + + self.league_score_rank = decode_int(data, off) + off += INT_OFF + + self.nick_name, new_off = decode_str(data, off) + off += new_off + + self.setting_title_id = decode_int(data, off) + off += INT_OFF + + self.favorite_hero_log_id = decode_int(data, off) + off += INT_OFF + + self.favorite_hero_log_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.favorite_support_log_id = decode_int(data, off) + off += INT_OFF + + self.favorite_support_log_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.total_league_point = decode_int(data, off) + off += INT_OFF + + self.have_league_score = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, profile: Dict) -> "DefragMatchRankingUserData": + ret = cls(b"\x00" * 34, 0) + ret.league_point_rank = 0 + ret.league_score_rank = 0 + ret.nick_name = profile['nick_name'] + ret.setting_title_id = profile['setting_title_id'] + ret.favorite_hero_log_id = profile['fav_hero'] + ret.favorite_hero_log_awakening_stage = 0 + ret.favorite_support_log_id = 0 + ret.favorite_support_log_awakening_stage = 0 + ret.total_league_point = 0 + ret.have_league_score = 0 + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.league_point_rank) \ + + encode_int(self.league_score_rank) \ + + encode_str(self.nick_name) \ + + encode_int(self.setting_title_id) \ + + encode_int(self.favorite_hero_log_id) \ + + encode_short(self.favorite_hero_log_awakening_stage) \ + + encode_int(self.favorite_support_log_id) \ + + encode_short(self.favorite_support_log_awakening_stage) \ + + encode_int(self.total_league_point) \ + + encode_short(self.have_league_score) + +class DefragMatchLeaguePointRankingData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.rank = decode_int(data, off) + off += INT_OFF + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.store_name, new_off = decode_str(data, off) + off += new_off + + self.nick_name, new_off = decode_str(data, off) + off += new_off + + self.setting_title_id = decode_int(data, off) + off += INT_OFF + + self.favorite_hero_log_id = decode_int(data, off) + off += INT_OFF + + self.favorite_hero_log_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.favorite_support_log_id = decode_int(data, off) + off += INT_OFF + + self.favorite_support_log_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.class_num = decode_short(data, off) + off += SHORT_OFF + + self.total_league_point = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchLeaguePointRankingData": + ret = cls(b"\x00" * 42, 0) + ret.rank = data['Rank'] + ret.user_id = data['UserId'] + ret.store_id = data['StoreId'] + ret.store_name = data['StoreName'] + ret.nick_name = data['NickName'] + ret.setting_title_id = data['SettingTitleId'] + ret.favorite_hero_log_id = data['FavoriteHeroLogId'] + ret.favorite_hero_log_awakening_stage = data['FavoriteHeroLogAwakeningStage'] + ret.favorite_support_log_id = data['FavoriteSupportLogId'] + ret.favorite_support_log_awakening_stage = data['FavoriteSupportLogAwakeningStage'] + ret.class_num = data['ClassNum'] + ret.total_league_point = data['TotalLeaguePoint'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.rank) \ + + encode_str(self.user_id) \ + + encode_str(self.store_id) \ + + encode_str(self.store_name) \ + + encode_str(self.nick_name) \ + + encode_int(self.setting_title_id) \ + + encode_int(self.favorite_hero_log_id) \ + + encode_short(self.favorite_hero_log_awakening_stage) \ + + encode_int(self.favorite_support_log_id) \ + + encode_short(self.favorite_support_log_awakening_stage) \ + + encode_short(self.class_num) \ + + encode_int(self.total_league_point) + +class DefragMatchLeagueScoreRankingList(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.rank = decode_byte(data, off) + off += BYTE_OFF + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.store_id, new_off = decode_str(data, off) + off += new_off + + self.store_name, new_off = decode_str(data, off) + off += new_off + + self.nick_name, new_off = decode_str(data, off) + off += new_off + + self.setting_title_id = decode_int(data, off) + off += INT_OFF + + self.favorite_hero_log_id = decode_int(data, off) + off += INT_OFF + + self.favorite_hero_log_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.favorite_support_log_id = decode_int(data, off) + off += INT_OFF + + self.favorite_support_log_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.class_num = decode_short(data, off) + off += SHORT_OFF + + self.have_league_score = decode_short(data, off) + off += SHORT_OFF + + @classmethod + def from_args(cls) -> "DefragMatchLeagueScoreRankingList": + return cls(b"\x00" * 40, 0) + + def make(self) -> bytes: + ret = encode_int(self.rank) + ret += encode_str(self.user_id) + ret += encode_str(self.store_id) + ret += encode_str(self.store_name) + ret += encode_str(self.nick_name) + + ret += encode_int(self.setting_title_id) + ret += encode_int(self.favorite_hero_log_id) + ret += encode_short(self.favorite_hero_log_awakening_stage) + ret += encode_int(self.favorite_support_log_id) + ret += encode_short(self.favorite_support_log_awakening_stage) + ret += encode_short(self.class_num) + ret += encode_short(self.have_league_score) + return ret + +class AppVersionData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.version_app_id = decode_int(data, off) + off += INT_OFF + + self.applying_start_date, new_off = decode_date_str(data, off) + off += new_off + + @classmethod + def from_args(cls, app_ver: int, start_date: datetime) -> BaseHelper: + ret = cls(b"\x00" * 8, 0) + ret.version_app_id = app_ver + ret.applying_start_date = start_date + return ret + + def make(self) -> bytes: + ret = encode_int(self.version_app_id) + ret += encode_date_str(self.applying_start_date) + return ret + +class MatchingErrorData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = 0 + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.param_1, new_off = decode_str(data, off) + off += new_off + + self.param_2, new_off = decode_str(data, off) + off += new_off + + self.param_3, new_off = decode_str(data, off) + off += new_off + + self.param_4, new_off = decode_str(data, off) + off += new_off + + self.param_5, new_off = decode_str(data, off) + off += new_off + + self.param_6, new_off = decode_str(data, off) + off += new_off + + self.param_7, new_off = decode_str(data, off) + off += new_off + + self.param_8, new_off = decode_str(data, off) + off += new_off + + self.param_9, new_off = decode_str(data, off) + off += new_off + + self.param_10, new_off = decode_str(data, off) + off += new_off + + self.error_occurred_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + def __str__(self) -> str: + s = f"User: {self.user_id} || " + s += f"Params: {self.param_1} {self.param_2} {self.param_3} {self.param_4} {self.param_5} {self.param_6} {self.param_7} {self.param_8} {self.param_9} {self.param_10} || " + s += f"Date: {self.error_occurred_date}" + return s + +class ReadProfileCard(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.profile_card_code, new_off = decode_str(data, off) # ID of the QR code + off += new_off + self.nick_name = decode_str(data, off) + off += new_off + + self.rank_num = decode_short(data, off) #short + off += SHORT_OFF + self.setting_title_id = decode_int(data, off) #int + off += INT_OFF + self.skill_id = decode_short(data, off) #short + off += SHORT_OFF + self.hero_log_hero_log_id = decode_int(data, off) #int + off += INT_OFF + self.hero_log_log_level = decode_short(data, off) #short + off += SHORT_OFF + self.hero_log_awakening_stage = decode_short(data, off) #short + off += SHORT_OFF + + self.hero_log_property1_property_id = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property1_value1 = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property1_value2 = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property2_property_id = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property2_value1 = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property2_value2 = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property3_property_id = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property3_value1 = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property3_value2 = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property4_property_id = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property4_value1 = decode_int(data, off) #int + off += INT_OFF + self.hero_log_property4_value2 = decode_int(data, off) #int + off += INT_OFF + + self.main_weapon_equipment_id = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_enhancement_value = decode_short(data, off) #short + off += SHORT_OFF + self.main_weapon_awakening_stage = decode_short(data, off) #short + off += SHORT_OFF + + self.main_weapon_property1_property_id = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property1_value1 = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property1_value2 = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property2_property_id = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property2_value1 = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property2_value2 = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property3_property_id = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property3_value1 = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property3_value2 = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property4_property_id = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property4_value1 = decode_int(data, off) #int + off += INT_OFF + self.main_weapon_property4_value2 = decode_int(data, off) #int + off += INT_OFF + + self.sub_equipment_equipment_id = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_enhancement_value = decode_short(data, off) #short + off += SHORT_OFF + self.sub_equipment_awakening_stage = decode_short(data, off) #short + off += SHORT_OFF + + self.sub_equipment_property1_property_id = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property1_value1 = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property1_value2 = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property2_property_id = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property2_value1 = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property2_value2 = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property3_property_id = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property3_value1 = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property3_value2 = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property4_property_id = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property4_value1 = decode_int(data, off) #int + off += INT_OFF + self.sub_equipment_property4_value2 = decode_int(data, off) #int + off += INT_OFF + + self.holographic_flag = decode_byte(data, off) #byte + off += BYTE_OFF + self._sz = off - offset + + @classmethod + def from_args(cls, code: str, player_name: str) -> "ReadProfileCard": + resp = cls(b"\x00" * 44, 0) + resp.profile_card_code = code # ID of the QR code + resp.nick_name = player_name + resp.rank_num = 1 #short + resp.setting_title_id = 20005 #int + resp.skill_id = 0 #short + resp.hero_log_hero_log_id = 118000230 #int + resp.hero_log_log_level = 1 #short + resp.hero_log_awakening_stage = 1 #short + + resp.hero_log_property1_property_id = 0 #int + resp.hero_log_property1_value1 = 0 #int + resp.hero_log_property1_value2 = 0 #int + resp.hero_log_property2_property_id = 0 #int + resp.hero_log_property2_value1 = 0 #int + resp.hero_log_property2_value2 = 0 #int + resp.hero_log_property3_property_id = 0 #int + resp.hero_log_property3_value1 = 0 #int + resp.hero_log_property3_value2 = 0 #int + resp.hero_log_property4_property_id = 0 #int + resp.hero_log_property4_value1 = 0 #int + resp.hero_log_property4_value2 = 0 #int + + resp.main_weapon_equipment_id = 0 #int + resp.main_weapon_enhancement_value = 0 #short + resp.main_weapon_awakening_stage = 0 #short + + resp.main_weapon_property1_property_id = 0 #int + resp.main_weapon_property1_value1 = 0 #int + resp.main_weapon_property1_value2 = 0 #int + resp.main_weapon_property2_property_id = 0 #int + resp.main_weapon_property2_value1 = 0 #int + resp.main_weapon_property2_value2 = 0 #int + resp.main_weapon_property3_property_id = 0 #int + resp.main_weapon_property3_value1 = 0 #int + resp.main_weapon_property3_value2 = 0 #int + resp.main_weapon_property4_property_id = 0 #int + resp.main_weapon_property4_value1 = 0 #int + resp.main_weapon_property4_value2 = 0 #int + + resp.sub_equipment_equipment_id = 0 #int + resp.sub_equipment_enhancement_value = 0 #short + resp.sub_equipment_awakening_stage = 0 #short + + resp.sub_equipment_property1_property_id = 0 #int + resp.sub_equipment_property1_value1 = 0 #int + resp.sub_equipment_property1_value2 = 0 #int + resp.sub_equipment_property2_property_id = 0 #int + resp.sub_equipment_property2_value1 = 0 #int + resp.sub_equipment_property2_value2 = 0 #int + resp.sub_equipment_property3_property_id = 0 #int + resp.sub_equipment_property3_value1 = 0 #int + resp.sub_equipment_property3_value2 = 0 #int + resp.sub_equipment_property4_property_id = 0 #int + resp.sub_equipment_property4_value1 = 0 #int + resp.sub_equipment_property4_value2 = 0 #int + + resp.holographic_flag = 0 #byte + return resp + +class HeroLogUserData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.user_hero_log_id, new_off = decode_str(data, off) + off += new_off + self.hero_log_id = decode_int(data, off) + off += INT_OFF + self.log_level = decode_short(data, off) + off += SHORT_OFF + self.max_log_level_extended_num = decode_short(data, off) + off += SHORT_OFF + self.log_exp = decode_int(data, off) + off += INT_OFF + self.possible_awakening_flag = decode_byte(data, off) + off += BYTE_OFF + self.awakening_stage = decode_short(data, off) + off += SHORT_OFF + self.awakening_exp = decode_int(data, off) + off += INT_OFF + self.skill_slot_correction_value = decode_byte(data, off) + off += BYTE_OFF + self.last_set_skill_slot1_skill_id = decode_short(data, off) + off += SHORT_OFF + self.last_set_skill_slot2_skill_id = decode_short(data, off) + off += SHORT_OFF + self.last_set_skill_slot3_skill_id = decode_short(data, off) + off += SHORT_OFF + self.last_set_skill_slot4_skill_id = decode_short(data, off) + off += SHORT_OFF + self.last_set_skill_slot5_skill_id = decode_short(data, off) + off += SHORT_OFF + self.property1_property_id = decode_int(data, off) + off += INT_OFF + self.property1_value1 = decode_int(data, off) + off += INT_OFF + self.property1_value2 = decode_int(data, off) + off += INT_OFF + self.property2_property_id = decode_int(data, off) + off += INT_OFF + self.property2_value1 = decode_int(data, off) + off += INT_OFF + self.property2_value2 = decode_int(data, off) + off += INT_OFF + self.property3_property_id = decode_int(data, off) + off += INT_OFF + self.property3_value1 = decode_int(data, off) + off += INT_OFF + self.property3_value2 = decode_int(data, off) + off += INT_OFF + self.property4_property_id = decode_int(data, off) + off += INT_OFF + self.property4_value1 = decode_int(data, off) + off += INT_OFF + self.property4_value2 = decode_int(data, off) + off += INT_OFF + self.converted_card_num = decode_short(data, off) + off += SHORT_OFF + self.shop_purchase_flag = decode_byte(data, off) + off += BYTE_OFF + self.protect_flag = decode_byte(data, off) + off += BYTE_OFF + + get_date, new_off = decode_str(data, off) + off += new_off + self.get_date = prs_dt(get_date) + self.sz = off - offset + + @classmethod + def from_args(cls, hero_data: Dict) -> "HeroLogUserData": + ret = cls(b"\x00" * 90, 0) + # Seems user_hero_log_id is a globally unique identifier, while hero_log_id identifies the hero + ret.user_hero_log_id = f"{hero_data['id']}" + ret.hero_log_id = hero_data['hero_log_id'] + + ret.log_level = hero_data['log_level'] + ret.max_log_level_extended_num = hero_data['max_level_extend_num'] + ret.log_exp = hero_data['log_exp'] + + ret.possible_awakening_flag = hero_data['is_awakenable'] + ret.awakening_stage = hero_data['awakening_stage'] + ret.awakening_exp = hero_data['awakening_exp'] + + ret.skill_slot_correction_value = 0 # Allows unlocking skill slot 4 and 5 early + ret.last_set_skill_slot1_skill_id = hero_data['skill_slot1_skill_id'] + ret.last_set_skill_slot2_skill_id = hero_data['skill_slot2_skill_id'] + ret.last_set_skill_slot3_skill_id = hero_data['skill_slot3_skill_id'] + ret.last_set_skill_slot4_skill_id = hero_data['skill_slot4_skill_id'] + ret.last_set_skill_slot5_skill_id = hero_data['skill_slot5_skill_id'] + + ret.property1_property_id = hero_data['property1_property_id'] + ret.property1_value1 = hero_data['property1_value1'] + ret.property1_value2 = hero_data['property1_value2'] + ret.property2_property_id = hero_data['property2_property_id'] + ret.property2_value1 = hero_data['property2_value1'] + ret.property2_value2 = hero_data['property2_value2'] + ret.property3_property_id = hero_data['property2_property_id'] + ret.property3_value1 = hero_data['property3_value1'] + ret.property3_value2 = hero_data['property3_value2'] + ret.property4_property_id = hero_data['property2_property_id'] + ret.property4_value1 = hero_data['property4_value1'] + ret.property4_value2 = hero_data['property4_value2'] + + ret.converted_card_num = hero_data['converted_card_num'] + ret.shop_purchase_flag = hero_data['is_shop_purchase'] + ret.protect_flag = hero_data['is_protect'] + ret.get_date = hero_data['get_date'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_hero_log_id) \ + + encode_int(self.hero_log_id) \ + + encode_short(self.log_level) \ + + encode_short(self.max_log_level_extended_num) \ + + encode_int(self.log_exp) \ + + encode_byte(self.possible_awakening_flag) \ + + encode_short(self.awakening_stage) \ + + encode_int(self.awakening_exp) \ + + encode_byte(self.skill_slot_correction_value) \ + + encode_short(self.last_set_skill_slot1_skill_id) \ + + encode_short(self.last_set_skill_slot2_skill_id) \ + + encode_short(self.last_set_skill_slot3_skill_id) \ + + encode_short(self.last_set_skill_slot4_skill_id) \ + + encode_short(self.last_set_skill_slot5_skill_id) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) \ + + encode_short(self.converted_card_num) \ + + encode_byte(self.shop_purchase_flag) \ + + encode_byte(self.protect_flag) \ + + encode_date_str(self.get_date) + +class YuiMedalBonusUserData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.elapsed_days = decode_int(data, off) + off += INT_OFF + self.loop_num = decode_int(data, off) + off += INT_OFF + + last_check_date, new_off = decode_str(data, off) + off += new_off + self.last_check_date = prs_dt(last_check_date) + + last_get_date, new_off = decode_str(data, off) + off += new_off + self.last_get_date = prs_dt(last_get_date) + + self._sz = off - offset + + @classmethod + def from_args(cls, elapsed_days: int = 0, loop_num: int = 0) -> BaseHelper: + ret = cls(b"\x00" * 996, 0) + ret.elapsed_days = elapsed_days + ret.loop_num = loop_num + ret.last_check_date = datetime.fromtimestamp(0) + ret.last_get_date = datetime.fromtimestamp(0) + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.elapsed_days) \ + + encode_int(self.loop_num) \ + + encode_date_str(self.last_check_date) \ + + encode_date_str(self.last_get_date) + +class UserBasicData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.user_type = decode_short(data, off) + off += SHORT_OFF + + self.nick_name, new_off = decode_str(data, off) + off += new_off + + self.rank_num = decode_short(data, off) + off += SHORT_OFF + + self.rank_exp = decode_int(data, off) + off += INT_OFF + + self.own_col = decode_int(data, off) + off += INT_OFF + + self.own_vp = decode_int(data, off) + off += INT_OFF + + self.own_yui_medal = decode_int(data, off) + off += INT_OFF + + self.setting_title_id = decode_int(data, off) + off += INT_OFF + + self.favorite_user_hero_log_id, new_off = decode_str(data, off) + off += new_off + + self.favorite_user_support_log_id, new_off = decode_str(data, off) + off += new_off + + self.my_store_id, new_off = decode_str(data, off) + off += new_off + + self.my_store_name, new_off = decode_str(data, off) + off += new_off + + user_reg_date, new_off = decode_str(data, off) + self.user_reg_date = prs_dt(user_reg_date) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, profile_data: Dict, shop_name: str = "ARTEMiS") -> "UserBasicData": + ret = cls(b"\0" * 52, 0) + ret.user_type = profile_data['user_type'] + ret.nick_name = profile_data['nick_name'] + ret.rank_num = profile_data['rank_num'] + ret.rank_exp = profile_data['rank_exp'] + ret.own_col = profile_data['own_col'] + ret.own_vp = profile_data['own_vp'] + ret.own_yui_medal = profile_data['own_yui_medal'] + ret.setting_title_id = profile_data['setting_title_id'] + ret.favorite_user_hero_log_id = profile_data['fav_hero'] + ret.favorite_user_support_log_id = "" # TODO: Supports + ret.my_store_id = "JPN0" + f"{profile_data['my_shop']:04X}" if profile_data["my_shop"] else "0" + ret.my_store_name = shop_name + ret.user_reg_date = profile_data['when_register'] if profile_data['when_register'] else prs_dt("20230101120000") + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.user_type) \ + + encode_str(self.nick_name) \ + + encode_short(self.rank_num) \ + + encode_int(self.rank_exp) \ + + encode_int(self.own_col) \ + + encode_int(self.own_vp) \ + + encode_int(self.own_yui_medal) \ + + encode_int(self.setting_title_id) \ + + encode_str(self.favorite_user_hero_log_id) \ + + encode_str(self.favorite_user_support_log_id) \ + + encode_str(self.my_store_id) \ + + encode_str(self.my_store_name) \ + + encode_date_str(self.user_reg_date) \ + +class VpGashaTicketData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.remaining_own_vp_gasha_ticket = decode_int(data, off) + off += INT_OFF + + self.expire_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, ticket_remain: int, exp_date: datetime) -> "VpGashaTicketData": + ret = cls(b"\x00" * 8, 0) + ret.remaining_own_vp_gasha_ticket = ticket_remain + ret.expire_date = exp_date + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.remaining_own_vp_gasha_ticket) \ + + encode_date_str(self.expire_date) + +class BeginnerMissionUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ad_confirm_flag = decode_byte(data, off) + off += BYTE_OFF + + self.ad_confirm_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, confirm_date: datetime, confirm_flg: bool = False) -> "BeginnerMissionUserData": + ret = cls(b"\x00" * 99, 0) + ret.ad_confirm_flag = confirm_flg + ret.ad_confirm_date = confirm_date + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.ad_confirm_flag) \ + + encode_date_str(self.ad_confirm_date) + +class EquipmentUserData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.user_equipment_id, new_off = decode_str(data, off) + off += new_off + + self.equipment_id = decode_int(data, off) + off += INT_OFF + self.enhancement_value = decode_short(data, off) + off += SHORT_OFF + self.max_enhancement_value_extended_num = decode_short(data, off) + off += SHORT_OFF + self.enhancement_exp = decode_int(data, off) + off += INT_OFF + self.possible_awakening_flag = decode_byte(data, off) + off += BYTE_OFF + self.awakening_stage = decode_short(data, off) + off += SHORT_OFF + self.awakening_exp = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + self.property1_value1 = decode_int(data, off) + off += INT_OFF + self.property1_value2 = decode_int(data, off) + off += INT_OFF + self.property2_property_id = decode_int(data, off) + off += INT_OFF + self.property2_value1 = decode_int(data, off) + off += INT_OFF + self.property2_value2 = decode_int(data, off) + off += INT_OFF + self.property3_property_id = decode_int(data, off) + off += INT_OFF + self.property3_value1 = decode_int(data, off) + off += INT_OFF + self.property3_value2 = decode_int(data, off) + off += INT_OFF + self.property4_property_id = decode_int(data, off) + off += INT_OFF + self.property4_value1 = decode_int(data, off) + off += INT_OFF + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self.converted_card_num = decode_short(data, off) + off += SHORT_OFF + self.shop_purchase_flag = decode_byte(data, off) + off += BYTE_OFF + self.protect_flag = decode_byte(data, off) + off += BYTE_OFF + + self.get_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, equip_data: Dict) -> "EquipmentUserData": + ret = cls(b"\x00" * 79, 0) + ret.user_equipment_id = str(equip_data['id']) + ret.equipment_id = equip_data['equipment_id'] + ret.enhancement_value = equip_data['enhancement_value'] + ret.max_enhancement_value_extended_num = 0 #equip_data['max_enhancement_value_extended_num'] TODO: This + ret.enhancement_exp = equip_data['enhancement_exp'] + ret.possible_awakening_flag = equip_data['possible_awakening_flag'] + ret.awakening_stage = equip_data['awakening_stage'] + ret.awakening_exp = equip_data['awakening_exp'] + ret.property1_property_id = equip_data['property1_property_id'] + ret.property1_value1 = equip_data['property1_value1'] + ret.property1_value2 = equip_data['property1_value2'] + ret.property2_property_id = equip_data['property2_property_id'] + ret.property2_value1 = equip_data['property2_value1'] + ret.property2_value2 = equip_data['property2_value2'] + ret.property3_property_id = equip_data['property2_property_id'] + ret.property3_value1 = equip_data['property3_value1'] + ret.property3_value2 = equip_data['property3_value2'] + ret.property4_property_id = equip_data['property2_property_id'] + ret.property4_value1 = equip_data['property4_value1'] + ret.property4_value2 = equip_data['property4_value2'] + ret.converted_card_num = equip_data['converted_card_num'] + ret.shop_purchase_flag = equip_data['is_shop_purchase'] + ret.protect_flag = equip_data['is_protect'] + ret.get_date = equip_data['get_date'] + return ret + + def make(self) -> bytes: + return encode_str(self.user_equipment_id) \ + + encode_int(self.equipment_id) \ + + encode_short(self.enhancement_value) \ + + encode_short(self.max_enhancement_value_extended_num) \ + + encode_int(self.enhancement_exp) \ + + encode_byte(self.possible_awakening_flag) \ + + encode_short(self.awakening_stage) \ + + encode_int(self.awakening_exp) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) \ + + encode_short(self.converted_card_num) \ + + encode_byte(self.shop_purchase_flag) \ + + encode_byte(self.protect_flag) \ + + encode_date_str(self.get_date) + +class ItemUserData(BaseHelper): + def __init__(self, data: bytes, offset: int) -> None: + super().__init__(data, offset) + off = offset + self.user_item_id, new_off = decode_str(data, off) + off += new_off + self.item_id = decode_int(data, off) + off += INT_OFF + self.protect_flag = decode_byte(data, off) + off += BYTE_OFF + self.get_date = decode_date_str(data, off) + + self._sz = off - offset + + @classmethod + def from_args(cls, item_data: Dict) -> BaseHelper: + ret = cls(b"\x00" * 993, 0) + ret.user_item_id = str(item_data['id']) + ret.item_id = item_data['item_id'] + ret.protect_flag = 0 + ret.get_date = item_data['get_date'] + return ret + + def make(self) -> bytes: + return encode_str(self.user_item_id) \ + + encode_int(self.item_id) \ + + encode_byte(self.protect_flag) \ + + encode_date_str(self.get_date) + +class SupportLogUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.user_support_log_id, new_off = decode_str(data, off) + off += new_off + + self.support_log_id = decode_int(data, off) + off += INT_OFF + + self.possible_awakening_flag = decode_byte(data, off) + off += BYTE_OFF + + self.awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.awakening_exp = decode_int(data, off) + off += INT_OFF + + self.converted_card_num = decode_short(data, off) + off += SHORT_OFF + + self.shop_purchase_flag = decode_byte(data, off) + off += BYTE_OFF + + self.protect_flag = decode_byte(data, off) + off += BYTE_OFF + + self.get_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, user_support_log: str, support_log: int) -> "SupportLogUserData": + ret = cls(b"\x00" * 23, 0) + ret.user_support_log_id = user_support_log + ret.support_log_id = support_log + ret.possible_awakening_flag = 0 + ret.awakening_stage = 0 + ret.awakening_exp = 0 + ret.converted_card_num = 0 + ret.shop_purchase_flag = 0 + ret.protect_flag = 0 + ret.get_date = prs_dt() + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_support_log_id) \ + + encode_int(self.support_log_id) \ + + encode_byte(self.possible_awakening_flag) \ + + encode_short(self.awakening_stage) \ + + encode_int(self.awakening_exp) \ + + encode_short(self.converted_card_num) \ + + encode_byte(self.shop_purchase_flag) \ + + encode_byte(self.protect_flag) \ + + encode_date_str(self.get_date) + +class EventItemUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.user_event_item_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.event_item_id = decode_int(data, off) + off += INT_OFF + + self.own_num = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EventItemUserData": + ret = cls(b"\x00" * 996, 0) + ret.user_event_item_id = data['UserEventItemId'] + ret.user_id = data['UserId'] + ret.event_item_id = data['EventItemId'] + ret.own_num = data['OwnNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_event_item_id) \ + + encode_str(self.user_id) \ + + encode_int(self.event_item_id) \ + + encode_int(self.own_num) + +class GashaMedalUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.user_gasha_medal_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.gasha_medal_id = decode_int(data, off) + off += INT_OFF + + self.own_num = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaMedalUserData": + ret = cls(b"\x00" * 996, 0) + ret.user_gasha_medal_id = data['UserGashaMedalId'] + ret.user_id = data['UserId'] + ret.gasha_medal_id = data['GashaMedalId'] + ret.own_num = data['OwnNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_gasha_medal_id) \ + + encode_str(self.user_id) \ + + encode_int(self.gasha_medal_id) \ + + encode_int(self.own_num) + +class TitleUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.user_title_id, new_off = decode_str(data, off) + off += new_off + + self.title_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, user_title_id: str, title_id: int) -> "TitleUserData": + ret = cls(b"\x00" * 8, 0) + ret.user_title_id = user_title_id + ret.title_id = title_id + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_title_id) \ + + encode_int(self.title_id) + +class PlayerRankData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.player_rank_id = decode_short(data, off) + off += SHORT_OFF + + self.total_exp = decode_int(data, off) + off += INT_OFF + + self.storage = decode_short(data, off) + off += SHORT_OFF + + self.team_preset = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "PlayerRankData": + ret = cls(b"\x00" * 99, 0) + ret.player_rank_id = data['PlayerRankId'] + ret.total_exp = data['TotalExp'] + ret.storage = data['Storage'] + ret.team_preset = data['TeamPreset'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.player_rank_id) \ + + encode_int(self.total_exp) \ + + encode_short(self.storage) \ + + encode_short(self.team_preset) + +class TitleData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.title_id = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self.requirement = decode_int(data, off) + off += INT_OFF + + self.value1 = decode_int(data, off) + off += INT_OFF + + self.value2 = decode_int(data, off) + off += INT_OFF + + self.rank = decode_int(data, off) + off += INT_OFF + + self.image_file_path, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TitleData": + ret = cls(b"\x00" * 99, 0) + ret.title_id = data['TitleId'] + ret.display_name = data['DisplayName'] + ret.requirement = data['Requirement'] + ret.value1 = data['Value1'] + ret.value2 = data['Value2'] + ret.rank = data['Rank'] + ret.image_file_path = data['ImageFilePath'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.title_id) \ + + encode_str(self.display_name) \ + + encode_int(self.requirement) \ + + encode_int(self.value1) \ + + encode_int(self.value2) \ + + encode_int(self.rank) \ + + encode_str(self.image_file_path) + +class FragmentData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.fragment_id = decode_short(data, off) + off += SHORT_OFF + + self.exp = decode_int(data, off) + off += INT_OFF + + self.comment_id, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "FragmentData": + ret = cls(b"\x00" * 99, 0) + ret.fragment_id = data['FragmentId'] + ret.exp = data['Exp'] + ret.comment_id = data['CommentId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.fragment_id) \ + + encode_int(self.exp) \ + + encode_str(self.comment_id) + +class RewardTableData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.reward_table_id = decode_int(data, off) + off += INT_OFF + + self.reward_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.unanalyzed_log_grade_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_int(data, off) + off += INT_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_int(data, off) + off += INT_OFF + + self.strength_min = decode_int(data, off) + off += INT_OFF + + self.strength_max = decode_int(data, off) + off += INT_OFF + + self.property_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.rate = decode_int(data, off) + off += INT_OFF + + self.quest_info_display_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "RewardTableData": + ret = cls(b"\x00" * 99, 0) + ret.reward_table_id = data['RewardTableId'] + ret.reward_table_sub_id = data['RewardTableSubId'] + ret.unanalyzed_log_grade_id = data['UnanalyzedLogGradeId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength_min = data['StrengthMin'] + ret.strength_max = data['StrengthMax'] + ret.property_table_sub_id = data['PropertyTableSubId'] + ret.rate = data['Rate'] + ret.quest_info_display_flag = data['QuestInfoDisplayFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.reward_table_id) \ + + encode_int(self.reward_table_sub_id) \ + + encode_int(self.unanalyzed_log_grade_id) \ + + encode_int(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_int(self.common_reward_num) \ + + encode_int(self.strength_min) \ + + encode_int(self.strength_max) \ + + encode_int(self.property_table_sub_id) \ + + encode_int(self.rate) \ + + encode_byte(self.quest_info_display_flag) + +class RewardSetData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.reward_set_id = decode_int(data, off) + off += INT_OFF + + self.reward_set_sub_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_int(data, off) + off += INT_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_int(data, off) + off += INT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "RewardSetData": + ret = cls(b"\x00" * 99, 0) + ret.reward_set_id = data['RewardSetId'] + ret.reward_set_sub_id = data['RewardSetSubId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.reward_set_id) \ + + encode_int(self.reward_set_sub_id) \ + + encode_int(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_int(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class UnanalyzedLogGradeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.unanalyzed_log_grade_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.comment_id, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "UnanalyzedLogGradeData": + ret = cls(b"\x00" * 99, 0) + ret.unanalyzed_log_grade_id = data['UnanalyzedLogGradeId'] + ret.name = data['Name'] + ret.comment_id = data['CommentId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.unanalyzed_log_grade_id) \ + + encode_str(self.name) \ + + encode_str(self.comment_id) + +class AppointLeaderParamData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.appoint_leader_param_id = decode_short(data, off) + off += SHORT_OFF + + self.initial_synchro_rate = decode_int(data, off) + off += INT_OFF + + self.appoint_leader_increment_synchro_rate = decode_int(data, off) + off += INT_OFF + + self.awakening_increment_synchro_rate = decode_int(data, off) + off += INT_OFF + + self.foil_add_synchro_rate = decode_int(data, off) + off += INT_OFF + + self.appoint_leader_trust_bonus = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "AppointLeaderParamData": + ret = cls(b"\x00" * 99, 0) + ret.appoint_leader_param_id = data['AppointLeaderParamId'] + ret.initial_synchro_rate = data['InitialSynchroRate'] + ret.appoint_leader_increment_synchro_rate = data['AppointLeaderIncrementSynchroRate'] + ret.awakening_increment_synchro_rate = data['AwakeningIncrementSynchroRate'] + ret.foil_add_synchro_rate = data['FoilAddSynchroRate'] + ret.appoint_leader_trust_bonus = data['AppointLeaderTrustBonus'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.appoint_leader_param_id) \ + + encode_int(self.initial_synchro_rate) \ + + encode_int(self.appoint_leader_increment_synchro_rate) \ + + encode_int(self.awakening_increment_synchro_rate) \ + + encode_int(self.foil_add_synchro_rate) \ + + encode_int(self.appoint_leader_trust_bonus) + +class AppointLeaderEffectData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.appoint_leader_effect_id = decode_short(data, off) + off += SHORT_OFF + + self.chara_id = decode_short(data, off) + off += SHORT_OFF + + self.info_text_format, new_off = decode_str(data, off) + off += new_off + + self.appoint_leader_effect_type_id = decode_short(data, off) + off += SHORT_OFF + + self.low_effect_value, new_off = decode_str(data, off) + off += new_off + + self.middle_effect_value, new_off = decode_str(data, off) + off += new_off + + self.high_effect_value, new_off = decode_str(data, off) + off += new_off + + self.max_effect_value, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "AppointLeaderEffectData": + ret = cls(b"\x00" * 99, 0) + ret.appoint_leader_effect_id = data['AppointLeaderEffectId'] + ret.chara_id = data['CharaId'] + ret.info_text_format = data['InfoTextFormat'] + ret.appoint_leader_effect_type_id = data['AppointLeaderEffectTypeId'] + ret.low_effect_value = data['LowEffectValue'] + ret.middle_effect_value = data['MiddleEffectValue'] + ret.high_effect_value = data['HighEffectValue'] + ret.max_effect_value = data['MaxEffectValue'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.appoint_leader_effect_id) \ + + encode_short(self.chara_id) \ + + encode_str(self.info_text_format) \ + + encode_short(self.appoint_leader_effect_type_id) \ + + encode_str(self.low_effect_value) \ + + encode_str(self.middle_effect_value) \ + + encode_str(self.high_effect_value) \ + + encode_str(self.max_effect_value) + +class AppointLeaderEffectTypeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.appoint_leader_effect_type_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "AppointLeaderEffectTypeData": + ret = cls(b"\x00" * 99, 0) + ret.appoint_leader_effect_type_id = data['AppointLeaderEffectTypeId'] + ret.name = data['Name'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.appoint_leader_effect_type_id) \ + + encode_str(self.name) + +class RarityData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.rarity_id = decode_short(data, off) + off += SHORT_OFF + + self.require_col_my_card = decode_int(data, off) + off += INT_OFF + + self.require_col_other_card = decode_int(data, off) + off += INT_OFF + + self.require_medal_other_card = decode_int(data, off) + off += INT_OFF + + self.synthesis_exp_rate, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "RarityData": + ret = cls(b"\x00" * 99, 0) + ret.rarity_id = data['RarityId'] + ret.require_col_my_card = data['RequireColMyCard'] + ret.require_col_other_card = data['RequireColOtherCard'] + ret.require_medal_other_card = data['RequireMedalOtherCard'] + ret.synthesis_exp_rate = data['SynthesisExpRate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.rarity_id) \ + + encode_int(self.require_col_my_card) \ + + encode_int(self.require_col_other_card) \ + + encode_int(self.require_medal_other_card) \ + + encode_str(self.synthesis_exp_rate) + +class CompositionEventData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.composition_event_id = decode_short(data, off) + off += SHORT_OFF + + self.composition_exp_rate, new_off = decode_str(data, off) + off += new_off + + self.awakening_exp_rate, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "CompositionEventData": + ret = cls(b"\x00" * 99, 0) + ret.composition_event_id = data['CompositionEventId'] + ret.composition_exp_rate = data['CompositionExpRate'] + ret.awakening_exp_rate = data['AwakeningExpRate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.composition_event_id) \ + + encode_str(self.composition_exp_rate) \ + + encode_str(self.awakening_exp_rate) + +class CompositionParamData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.composition_param_id = decode_short(data, off) + off += SHORT_OFF + + self.use_value = decode_int(data, off) + off += INT_OFF + + self.max_extended_use_coef, new_off = decode_str(data, off) + off += new_off + + self.awakening_coef, new_off = decode_str(data, off) + off += new_off + + self.use_value_support_log = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "CompositionParamData": + ret = cls(b"\x00" * 99, 0) + ret.composition_param_id = data['CompositionParamId'] + ret.use_value = data['UseValue'] + ret.max_extended_use_coef = data['MaxExtendedUseCoef'] + ret.awakening_coef = data['AwakeningCoef'] + ret.use_value_support_log = data['UseValueSupportLog'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.composition_param_id) \ + + encode_int(self.use_value) \ + + encode_str(self.max_extended_use_coef) \ + + encode_str(self.awakening_coef) \ + + encode_int(self.use_value_support_log) + +class GamePlayPriceData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.game_play_price_id = decode_short(data, off) + off += SHORT_OFF + + self.player_rank_id = decode_short(data, off) + off += SHORT_OFF + + self.episode_ticket = decode_int(data, off) + off += INT_OFF + + self.trial_tower_ticket = decode_short(data, off) + off += SHORT_OFF + + self.custom_retry_ticket = decode_short(data, off) + off += SHORT_OFF + + self.subdue_ticket = decode_short(data, off) + off += SHORT_OFF + + self.continue_ticket = decode_short(data, off) + off += SHORT_OFF + + self.continue_credit = decode_short(data, off) + off += SHORT_OFF + + self.extend_time_ticket = decode_short(data, off) + off += SHORT_OFF + + self.reward_grade_up_ticket = decode_short(data, off) + off += SHORT_OFF + + self.reward_grade_up_credit = decode_short(data, off) + off += SHORT_OFF + + self.give_free_ticket = decode_short(data, off) + off += SHORT_OFF + + self.free_continue_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GamePlayPriceData": + ret = cls(b"\x00" * 99, 0) + ret.game_play_price_id = data['GamePlayPriceId'] + ret.player_rank_id = data['PlayerRankId'] + ret.episode_ticket = data['EpisodeTicket'] + ret.trial_tower_ticket = data['TrialTowerTicket'] + ret.custom_retry_ticket = data['CustomRetryTicket'] + ret.subdue_ticket = data['SubdueTicket'] + ret.continue_ticket = data['ContinueTicket'] + ret.continue_credit = data['ContinueCredit'] + ret.extend_time_ticket = data['ExtendTimeTicket'] + ret.reward_grade_up_ticket = data['RewardGradeUpTicket'] + ret.reward_grade_up_credit = data['RewardGradeUpCredit'] + ret.give_free_ticket = data['GiveFreeTicket'] + ret.free_continue_num = data['FreeContinueNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.game_play_price_id) \ + + encode_short(self.player_rank_id) \ + + encode_int(self.episode_ticket) \ + + encode_short(self.trial_tower_ticket) \ + + encode_short(self.custom_retry_ticket) \ + + encode_short(self.subdue_ticket) \ + + encode_short(self.continue_ticket) \ + + encode_short(self.continue_credit) \ + + encode_short(self.extend_time_ticket) \ + + encode_short(self.reward_grade_up_ticket) \ + + encode_short(self.reward_grade_up_credit) \ + + encode_short(self.give_free_ticket) \ + + encode_short(self.free_continue_num) + +class BuyTicketData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.buy_ticket_id = decode_int(data, off) + off += INT_OFF + + self.buy_ticket_pattern = decode_int(data, off) + off += INT_OFF + + self.credit_cnt = decode_int(data, off) + off += INT_OFF + + self.get_ticket_cnt = decode_int(data, off) + off += INT_OFF + + self.get_bonus_vp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BuyTicketData": + ret = cls(b"\x00" * 99, 0) + ret.buy_ticket_id = data['BuyTicketId'] + ret.buy_ticket_pattern = data['BuyTicketPattern'] + ret.credit_cnt = data['CreditCnt'] + ret.get_ticket_cnt = data['GetTicketCnt'] + ret.get_bonus_vp = data['GetBonusVp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.buy_ticket_id) \ + + encode_int(self.buy_ticket_pattern) \ + + encode_int(self.credit_cnt) \ + + encode_int(self.get_ticket_cnt) \ + + encode_int(self.get_bonus_vp) + +class TipsData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.tips_id = decode_int(data, off) + off += INT_OFF + + self.tips_category = decode_byte(data, off) + off += BYTE_OFF + + self.tips_text, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TipsData": + ret = cls(b"\x00" * 99, 0) + ret.tips_id = data['TipsId'] + ret.tips_category = data['TipsCategory'] + ret.tips_text = data['TipsText'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.tips_id) \ + + encode_byte(self.tips_category) \ + + encode_str(self.tips_text) + +class CapData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.cap_id = decode_short(data, off) + off += SHORT_OFF + + self.trust1_cap = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "CapData": + ret = cls(b"\x00" * 99, 0) + ret.cap_id = data['CapId'] + ret.trust1_cap = data['Trust1Cap'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.cap_id) \ + + encode_int(self.trust1_cap) + +class HeroLogData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.hero_log_id = decode_int(data, off) + off += INT_OFF + + self.chara_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.nickname, new_off = decode_str(data, off) + off += new_off + + self.rarity = decode_byte(data, off) + off += BYTE_OFF + + self.weapon_type_id = decode_short(data, off) + off += SHORT_OFF + + self.hero_log_role_id = decode_short(data, off) + off += SHORT_OFF + + self.costume_type_id = decode_short(data, off) + off += SHORT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.default_equipment_id1 = decode_int(data, off) + off += INT_OFF + + self.default_equipment_id2 = decode_int(data, off) + off += INT_OFF + + self.skill_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.hp_min = decode_int(data, off) + off += INT_OFF + + self.hp_max = decode_int(data, off) + off += INT_OFF + + self.str_min = decode_int(data, off) + off += INT_OFF + + self.str_max = decode_int(data, off) + off += INT_OFF + + self.vit_min = decode_int(data, off) + off += INT_OFF + + self.vit_max = decode_int(data, off) + off += INT_OFF + + self.int_min = decode_int(data, off) + off += INT_OFF + + self.int_max = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self.flavor_text, new_off = decode_str(data, off) + off += new_off + + self.sale_price = decode_int(data, off) + off += INT_OFF + + self.composition_exp = decode_int(data, off) + off += INT_OFF + + self.awakening_exp = decode_int(data, off) + off += INT_OFF + + self.slot4_unlock_level = decode_int(data, off) + off += INT_OFF + + self.slot5_unlock_level = decode_int(data, off) + off += INT_OFF + + self.cut_in_image, new_off = decode_str(data, off) + off += new_off + + self.cut_in_image_awake, new_off = decode_str(data, off) + off += new_off + + self.cut_in_upper_side_text = decode_byte(data, off) + off += BYTE_OFF + + self.chara_comment_image, new_off = decode_str(data, off) + off += new_off + + self.chara_comment_image_awake, new_off = decode_str(data, off) + off += new_off + + self.quest_start_introduce, new_off = decode_str(data, off) + off += new_off + + self.quest_start_introduce_awake, new_off = decode_str(data, off) + off += new_off + + self.quest_chara_icon, new_off = decode_str(data, off) + off += new_off + + self.quest_chara_icon_awake, new_off = decode_str(data, off) + off += new_off + + self.quest_chara_icon_loss, new_off = decode_str(data, off) + off += new_off + + self.quest_chara_icon_awake_loss, new_off = decode_str(data, off) + off += new_off + + self.collection_display_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.collection_empty_frame_display_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "HeroLogData": + ret = cls(b"\x00" * 99, 0) + ret.hero_log_id = data['HeroLogId'] + ret.chara_id = data['CharaId'] + ret.name = data['Name'] + ret.nickname = data['Nickname'] + ret.rarity = data['Rarity'] + ret.weapon_type_id = data['WeaponTypeId'] + ret.hero_log_role_id = data['HeroLogRoleId'] + ret.costume_type_id = data['CostumeTypeId'] + ret.unit_id = data['UnitId'] + ret.default_equipment_id1 = data['DefaultEquipmentId1'] + ret.default_equipment_id2 = data['DefaultEquipmentId2'] + ret.skill_table_sub_id = data['SkillTableSubId'] + ret.hp_min = data['HpMin'] + ret.hp_max = data['HpMax'] + ret.str_min = data['StrMin'] + ret.str_max = data['StrMax'] + ret.vit_min = data['VitMin'] + ret.vit_max = data['VitMax'] + ret.int_min = data['IntMin'] + ret.int_max = data['IntMax'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + ret.flavor_text = data['FlavorText'] + ret.sale_price = data['SalePrice'] + ret.composition_exp = data['CompositionExp'] + ret.awakening_exp = data['AwakeningExp'] + ret.slot4_unlock_level = data['Slot4UnlockLevel'] + ret.slot5_unlock_level = data['Slot5UnlockLevel'] + ret.cut_in_image = data['CutinImage'] + ret.cut_in_image_awake = data['CutinImageAwake'] + ret.cut_in_upper_side_text = data['CutinUpperSideText'] + ret.chara_comment_image = data['CharaCommentImage'] + ret.chara_comment_image_awake = data['CharaCommentImageAwake'] + ret.quest_start_introduce = data['QuestStartIntroduce'] + ret.quest_start_introduce_awake = data['QuestStartIntroduceAwake'] + ret.quest_chara_icon = data['QuestCharaIcon'] + ret.quest_chara_icon_awake = data['QuestCharaIconAwake'] + ret.quest_chara_icon_loss = data['QuestCharaIconLoss'] + ret.quest_chara_icon_awake_loss = data['QuestCharaIconAwakeLoss'] + ret.collection_display_start_date = data['CollectionDisplayStartDate'] + ret.collection_empty_frame_display_flag = data['CollectionEmptyFrameDisplayFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.hero_log_id) \ + + encode_short(self.chara_id) \ + + encode_str(self.name) \ + + encode_str(self.nickname) \ + + encode_byte(self.rarity) \ + + encode_short(self.weapon_type_id) \ + + encode_short(self.hero_log_role_id) \ + + encode_short(self.costume_type_id) \ + + encode_int(self.unit_id) \ + + encode_int(self.default_equipment_id1) \ + + encode_int(self.default_equipment_id2) \ + + encode_int(self.skill_table_sub_id) \ + + encode_int(self.hp_min) \ + + encode_int(self.hp_max) \ + + encode_int(self.str_min) \ + + encode_int(self.str_max) \ + + encode_int(self.vit_min) \ + + encode_int(self.vit_max) \ + + encode_int(self.int_min) \ + + encode_int(self.int_max) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) \ + + encode_str(self.flavor_text) \ + + encode_int(self.sale_price) \ + + encode_int(self.composition_exp) \ + + encode_int(self.awakening_exp) \ + + encode_int(self.slot4_unlock_level) \ + + encode_int(self.slot5_unlock_level) \ + + encode_str(self.cut_in_image) \ + + encode_str(self.cut_in_image_awake) \ + + encode_byte(self.cut_in_upper_side_text) \ + + encode_str(self.chara_comment_image) \ + + encode_str(self.chara_comment_image_awake) \ + + encode_str(self.quest_start_introduce) \ + + encode_str(self.quest_start_introduce_awake) \ + + encode_str(self.quest_chara_icon) \ + + encode_str(self.quest_chara_icon_awake) \ + + encode_str(self.quest_chara_icon_loss) \ + + encode_str(self.quest_chara_icon_awake_loss) \ + + encode_date_str(self.collection_display_start_date) \ + + encode_byte(self.collection_empty_frame_display_flag) + +class HeroLogLevelData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.level = decode_short(data, off) + off += SHORT_OFF + + self.require_exp = decode_int(data, off) + off += INT_OFF + + self.total_exp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "HeroLogLevelData": + ret = cls(b"\x00" * 99, 0) + ret.level = data['HeroLogLevelId'] + ret.require_exp = data['RequireExp'] + ret.total_exp = data['TotalExp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.level) \ + + encode_int(self.require_exp) \ + + encode_int(self.total_exp) + +class HeroLogRoleData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.hero_log_role_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "HeroLogRoleData": + ret = cls(b"\x00" * 99, 0) + ret.hero_log_role_id = data['HeroLogRoleId'] + ret.name = data['Name'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.hero_log_role_id) \ + + encode_str(self.name) + +class HeroLogTrustRankData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.rank = decode_short(data, off) + off += SHORT_OFF + + self.total_exp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "HeroLogTrustRankData": + ret = cls(b"\x00" * 99, 0) + ret.rank = data['HeroLogTrustRankId'] + ret.total_exp = data['TotalExp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.rank) \ + + encode_int(self.total_exp) + +class CharaData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.chara_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.roma, new_off = decode_str(data, off) + off += new_off + + self.gender = decode_short(data, off) + off += SHORT_OFF + + self.real_name, new_off = decode_str(data, off) + off += new_off + + self.comment, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "CharaData": + ret = cls(b"\x00" * 99, 0) + ret.chara_id = data['CharaId'] + ret.name = data['Name'] + ret.roma = data['Roma'] + ret.gender = data['Gender'] + ret.real_name = data['RealName'] + ret.comment = data['Comment'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.chara_id) \ + + encode_str(self.name) \ + + encode_str(self.roma) \ + + encode_short(self.gender) \ + + encode_str(self.real_name) \ + + encode_str(self.comment) + +class CharaFriendlyRankData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.rank = decode_short(data, off) + off += SHORT_OFF + + self.total_exp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "CharaFriendlyRankData": + ret = cls(b"\x00" * 99, 0) + ret.rank = data['CharaFriendlyRankId'] + ret.total_exp = data['TotalExp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.rank) \ + + encode_int(self.total_exp) + +class EquipmentData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.equipment_id = decode_int(data, off) + off += INT_OFF + + self.equipment_type = decode_byte(data, off) + off += BYTE_OFF + + self.weapon_type_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.rarity = decode_byte(data, off) + off += BYTE_OFF + + self.prefab, new_off = decode_str(data, off) + off += new_off + + self.power = decode_int(data, off) + off += INT_OFF + + self.strength_increment = decode_int(data, off) + off += INT_OFF + + self.skill_condition = decode_short(data, off) + off += SHORT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self.sale_price = decode_int(data, off) + off += INT_OFF + + self.composition_exp = decode_int(data, off) + off += INT_OFF + + self.awakening_exp = decode_int(data, off) + off += INT_OFF + + self.flavor_text, new_off = decode_str(data, off) + off += new_off + + self.collection_display_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.collection_empty_frame_display_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EquipmentData": + ret = cls(b"\x00" * 99, 0) + ret.equipment_id = data['EquipmentId'] + ret.equipment_type = data['EquipmentType'] + ret.weapon_type_id = data['WeaponTypeId'] + ret.name = data['Name'] + ret.rarity = data['Rarity'] + ret.prefab = data['Prefab'] + ret.power = data['Power'] + ret.strength_increment = data['StrengthIncrement'] + ret.skill_condition = data['SkillCondition'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + ret.sale_price = data['SalePrice'] + ret.composition_exp = data['CompositionExp'] + ret.awakening_exp = data['AwakeningExp'] + ret.flavor_text = data['FlavorText'] + ret.collection_display_start_date = data['CollectionDisplayStartDate'] + ret.collection_empty_frame_display_flag = data['CollectionEmptyFrameDisplayFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.equipment_id) \ + + encode_byte(self.equipment_type) \ + + encode_short(self.weapon_type_id) \ + + encode_str(self.name) \ + + encode_byte(self.rarity) \ + + encode_str(self.prefab) \ + + encode_int(self.power) \ + + encode_int(self.strength_increment) \ + + encode_short(self.skill_condition) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) \ + + encode_int(self.sale_price) \ + + encode_int(self.composition_exp) \ + + encode_int(self.awakening_exp) \ + + encode_str(self.flavor_text) \ + + encode_date_str(self.collection_display_start_date) \ + + encode_byte(self.collection_empty_frame_display_flag) + +class EquipmentLevelData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.level = decode_short(data, off) + off += SHORT_OFF + + self.require_exp = decode_int(data, off) + off += INT_OFF + + self.total_exp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EquipmentLevelData": + ret = cls(b"\x00" * 99, 0) + ret.level = data['EquipmentLevelId'] + ret.require_exp = data['RequireExp'] + ret.total_exp = data['TotalExp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.level) \ + + encode_int(self.require_exp) \ + + encode_int(self.total_exp) + +class WeaponTypeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.weapon_type_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.action_behaviour, new_off = decode_str(data, off) + off += new_off + + self.sound_behaviour, new_off = decode_str(data, off) + off += new_off + + self.physics_attr = decode_short(data, off) + off += SHORT_OFF + + self.main_equip = decode_short(data, off) + off += SHORT_OFF + + self.sub_equip1 = decode_short(data, off) + off += SHORT_OFF + + self.sub_equip2 = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "WeaponTypeData": + ret = cls(b"\x00" * 99, 0) + ret.weapon_type_id = data['WeaponTypeId'] + ret.name = data['Name'] + ret.action_behaviour = data['ActionBehaviour'] + ret.sound_behaviour = data['SoundBehaviour'] + ret.physics_attr = data['PhysicsAttr'] + ret.main_equip = data['MainEquip'] + ret.sub_equip1 = data['SubEquip1'] + ret.sub_equip2 = data['SubEquip2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.weapon_type_id) \ + + encode_str(self.name) \ + + encode_str(self.action_behaviour) \ + + encode_str(self.sound_behaviour) \ + + encode_short(self.physics_attr) \ + + encode_short(self.main_equip) \ + + encode_short(self.sub_equip1) \ + + encode_short(self.sub_equip2) + +class ItemData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.item_id = decode_int(data, off) + off += INT_OFF + + self.item_type = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.rarity = decode_byte(data, off) + off += BYTE_OFF + + self.value = decode_int(data, off) + off += INT_OFF + + self.property_id = decode_int(data, off) + off += INT_OFF + + self.property_value1_min, new_off = decode_str(data, off) + off += new_off + + self.property_value1_max, new_off = decode_str(data, off) + off += new_off + + self.property_value2_min, new_off = decode_str(data, off) + off += new_off + + self.property_value2_max, new_off = decode_str(data, off) + off += new_off + + self.flavor_text, new_off = decode_str(data, off) + off += new_off + + self.sale_price = decode_int(data, off) + off += INT_OFF + + self.item_icon, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ItemData": + ret = cls(b"\x00" * 99, 0) + ret.item_id = data['ItemId'] + ret.item_type = data['ItemTypeId'] + ret.name = data['Name'] + ret.rarity = data['Rarity'] + ret.value = data['Value'] + ret.property_id = data['PropertyId'] + ret.property_value1_min = data['PropertyValue1Min'] + ret.property_value1_max = data['PropertyValue1Max'] + ret.property_value2_min = data['PropertyValue2Min'] + ret.property_value2_max = data['PropertyValue2Max'] + ret.flavor_text = data['FlavorText'] + ret.sale_price = data['SalePrice'] + ret.item_icon = data['ItemIcon'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.item_id) \ + + encode_int(self.item_type) \ + + encode_str(self.name) \ + + encode_byte(self.rarity) \ + + encode_int(self.value) \ + + encode_int(self.property_id) \ + + encode_str(self.property_value1_min) \ + + encode_str(self.property_value1_max) \ + + encode_str(self.property_value2_min) \ + + encode_str(self.property_value2_max) \ + + encode_str(self.flavor_text) \ + + encode_int(self.sale_price) \ + + encode_str(self.item_icon) + +class ItemTypeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.item_type_id = decode_int(data, off) + off += INT_OFF + + self.item_type_name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ItemTypeData": + ret = cls(b"\x00" * 99, 0) + ret.item_type_id = data['ItemTypeId'] + ret.item_type_name = data['ItemTypeName'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.item_type_id) \ + + encode_str(self.item_type_name) + +class BuffItemData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.buff_item_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.flavor_text, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BuffItemData": + ret = cls(b"\x00" * 99, 0) + ret.buff_item_id = data['BuffItemId'] + ret.name = data['Name'] + ret.flavor_text = data['FlavorText'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.buff_item_id) \ + + encode_str(self.name) \ + + encode_str(self.flavor_text) + +class EnemyData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.enemy_id = decode_short(data, off) + off += SHORT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EnemyData": + ret = cls(b"\x00" * 99, 0) + ret.enemy_id = data['EnemyId'] + ret.unit_id = data['UnitId'] + ret.name = data['Name'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.enemy_id) \ + + encode_int(self.unit_id) \ + + encode_str(self.name) + +class EnemySetData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.enemy_set_id = decode_int(data, off) + off += INT_OFF + + self.enemy_set_sub_id = decode_int(data, off) + off += INT_OFF + + self.enemy_id = decode_int(data, off) + off += INT_OFF + + self.enemy_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EnemySetData": + ret = cls(b"\x00" * 99, 0) + ret.enemy_set_id = data['EnemySetId'] + ret.enemy_set_sub_id = data['EnemySetSubId'] + ret.enemy_id = data['EnemyId'] + ret.enemy_num = data['EnemyNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.enemy_set_id) \ + + encode_int(self.enemy_set_sub_id) \ + + encode_int(self.enemy_id) \ + + encode_short(self.enemy_num) + +class EnemyKindData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.enemy_kind_id = decode_int(data, off) + off += INT_OFF + + self.enemy_category_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.icon_filepath, new_off = decode_str(data, off) + off += new_off + + self.weak_physics_attr = decode_short(data, off) + off += SHORT_OFF + + self.weak_magic_attr = decode_short(data, off) + off += SHORT_OFF + + self.weak_text, new_off = decode_str(data, off) + off += new_off + + self.resist_text, new_off = decode_str(data, off) + off += new_off + + self.flavor_text, new_off = decode_str(data, off) + off += new_off + + self.collection_display_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.collection_empty_frame_display_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EnemyKindData": + ret = cls(b"\x00" * 99, 0) + ret.enemy_kind_id = data['EnemyKindId'] + ret.enemy_category_id = data['EnemyCategoryId'] + ret.name = data['Name'] + ret.icon_filepath = data['IconFilepath'] + ret.weak_physics_attr = data['WeakPhysicsAttr'] + ret.weak_magic_attr = data['WeakMagicAttr'] + ret.weak_text = data['WeakText'] + ret.resist_text = data['ResistText'] + ret.flavor_text = data['FlavorText'] + ret.collection_display_start_date = data['CollectionDisplayStartDate'] + ret.collection_empty_frame_display_flag = data['CollectionEmptyFrameDisplayFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.enemy_kind_id) \ + + encode_short(self.enemy_category_id) \ + + encode_str(self.name) \ + + encode_str(self.icon_filepath) \ + + encode_short(self.weak_physics_attr) \ + + encode_short(self.weak_magic_attr) \ + + encode_str(self.weak_text) \ + + encode_str(self.resist_text) \ + + encode_str(self.flavor_text) \ + + encode_date_str(self.collection_display_start_date) \ + + encode_byte(self.collection_empty_frame_display_flag) + +class EnemyCategoryData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.enemy_category_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.detail_text, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EnemyCategoryData": + ret = cls(b"\x00" * 99, 0) + ret.enemy_category_id = data['EnemyCategoryId'] + ret.name = data['Name'] + ret.detail_text = data['DetailText'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.enemy_category_id) \ + + encode_str(self.name) \ + + encode_str(self.detail_text) + +class UnitData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.prefab, new_off = decode_str(data, off) + off += new_off + + self.animator, new_off = decode_str(data, off) + off += new_off + + self.collision, new_off = decode_str(data, off) + off += new_off + + self.radius, new_off = decode_str(data, off) + off += new_off + + self.child_unit_id = decode_int(data, off) + off += INT_OFF + + self.stage_start_prefab, new_off = decode_str(data, off) + off += new_off + + self.stage_start_y, new_off = decode_str(data, off) + off += new_off + + self.stage_start_z, new_off = decode_str(data, off) + off += new_off + + self.stage_touch_y, new_off = decode_str(data, off) + off += new_off + + self.stage_touch_z, new_off = decode_str(data, off) + off += new_off + + self.playable = decode_byte(data, off) + off += BYTE_OFF + + self.monster = decode_byte(data, off) + off += BYTE_OFF + + self.gimmick = decode_byte(data, off) + off += BYTE_OFF + + self.anger = decode_byte(data, off) + off += BYTE_OFF + + self.commander = decode_byte(data, off) + off += BYTE_OFF + + self.isolated = decode_byte(data, off) + off += BYTE_OFF + + self.base_behaviour, new_off = decode_str(data, off) + off += new_off + + self.sound_behaviour, new_off = decode_str(data, off) + off += new_off + + self.weapon_type = decode_int(data, off) + off += INT_OFF + + self.poison_time_coef, new_off = decode_str(data, off) + off += new_off + + self.paralysis_time_coef, new_off = decode_str(data, off) + off += new_off + + self.sealed_time_coef, new_off = decode_str(data, off) + off += new_off + + self.question_time_coef, new_off = decode_str(data, off) + off += new_off + + self.blue_rose_time_coef, new_off = decode_str(data, off) + off += new_off + + self.charm_time_coef, new_off = decode_str(data, off) + off += new_off + + self.burning_time_coef, new_off = decode_str(data, off) + off += new_off + + self.invalid_quake = decode_byte(data, off) + off += BYTE_OFF + + self.guard_coef, new_off = decode_str(data, off) + off += new_off + + self.just_guard_time, new_off = decode_str(data, off) + off += new_off + + self.flip_hp = decode_short(data, off) + off += SHORT_OFF + + self.down_hp = decode_short(data, off) + off += SHORT_OFF + + self.flip_recover_time, new_off = decode_str(data, off) + off += new_off + + self.down_recover_time, new_off = decode_str(data, off) + off += new_off + + self.down_time, new_off = decode_str(data, off) + off += new_off + + self.loss_damage = decode_short(data, off) + off += SHORT_OFF + + self.loss_time, new_off = decode_str(data, off) + off += new_off + + self.move_speed, new_off = decode_str(data, off) + off += new_off + + self.move_speed_angry, new_off = decode_str(data, off) + off += new_off + + self.rot_speed, new_off = decode_str(data, off) + off += new_off + + self.rot_speed_angry, new_off = decode_str(data, off) + off += new_off + + self.range_min, new_off = decode_str(data, off) + off += new_off + + self.range_max, new_off = decode_str(data, off) + off += new_off + + self.range_min_angry, new_off = decode_str(data, off) + off += new_off + + self.range_max_angry, new_off = decode_str(data, off) + off += new_off + + self.back_step_speed, new_off = decode_str(data, off) + off += new_off + + self.back_step_speed_angry, new_off = decode_str(data, off) + off += new_off + + self.back_step_cool_time, new_off = decode_str(data, off) + off += new_off + + self.monster_type = decode_short(data, off) + off += SHORT_OFF + + self.monster_type_angry = decode_short(data, off) + off += SHORT_OFF + + self.vs_boss_type = decode_short(data, off) + off += SHORT_OFF + + self.boss_camera_height, new_off = decode_str(data, off) + off += new_off + + self.boss_camera_near_distance, new_off = decode_str(data, off) + off += new_off + + self.boss_camera_far_distance, new_off = decode_str(data, off) + off += new_off + + self.boss_camera_near_range, new_off = decode_str(data, off) + off += new_off + + self.boss_camera_far_range, new_off = decode_str(data, off) + off += new_off + + self.search_range, new_off = decode_str(data, off) + off += new_off + + self.search_angle, new_off = decode_str(data, off) + off += new_off + + self.lost_range, new_off = decode_str(data, off) + off += new_off + + self.home_range, new_off = decode_str(data, off) + off += new_off + + self.roar_id, new_off = decode_str(data, off) + off += new_off + + self.auto_attack_range, new_off = decode_str(data, off) + off += new_off + + self.auto_attack_interval, new_off = decode_str(data, off) + off += new_off + + self.find_priority = decode_short(data, off) + off += SHORT_OFF + + self.find_range_min = decode_short(data, off) + off += SHORT_OFF + + self.find_range_max = decode_short(data, off) + off += SHORT_OFF + + self.leave_range = decode_short(data, off) + off += SHORT_OFF + + self.show_arrow = decode_byte(data, off) + off += BYTE_OFF + + self.find_id, new_off = decode_str(data, off) + off += new_off + + self.leave_id, new_off = decode_str(data, off) + off += new_off + + self.defeat_id, new_off = decode_str(data, off) + off += new_off + + self.warfare_id, new_off = decode_str(data, off) + off += new_off + + self.angry_id, new_off = decode_str(data, off) + off += new_off + + self.bad_state_bone, new_off = decode_str(data, off) + off += new_off + + self.bad_state_offset_x, new_off = decode_str(data, off) + off += new_off + + self.bad_state_offset_y, new_off = decode_str(data, off) + off += new_off + + self.bad_state_offset_z, new_off = decode_str(data, off) + off += new_off + + self.drop_offset_y, new_off = decode_str(data, off) + off += new_off + + self.col_drop_percentage, new_off = decode_str(data, off) + off += new_off + + self.col_drop_amount = decode_short(data, off) + off += SHORT_OFF + + self.vp_drop_percentage, new_off = decode_str(data, off) + off += new_off + + self.vp_drop_amount = decode_short(data, off) + off += SHORT_OFF + + self.enemy_kind_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_league_point = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "UnitData": + ret = cls(b"\x00" * 99, 0) + ret.unit_id = data['UnitId'] + ret.name = data['Name'] + ret.prefab = data['Prefab'] + ret.animator = data['Animator'] + ret.collision = data['Collision'] + ret.radius = data['Radius'] + ret.child_unit_id = data['ChildUnitId'] + ret.stage_start_prefab = data['StageStartPrefab'] + ret.stage_start_y = data['StageStartY'] + ret.stage_start_z = data['StageStartZ'] + ret.stage_touch_y = data['StageTouchY'] + ret.stage_touch_z = data['StageTouchZ'] + ret.playable = data['Playable'] + ret.monster = data['Monster'] + ret.gimmick = data['Gimmick'] + ret.anger = data['Anger'] + ret.commander = data['Commander'] + ret.isolated = data['Isolated'] + ret.base_behaviour = data['BaseBehaviour'] + ret.sound_behaviour = data['SoundBehaviour'] + ret.weapon_type = data['WeaponType'] + ret.poison_time_coef = data['PoisonTimeCoef'] + ret.paralysis_time_coef = data['ParalysisTimeCoef'] + ret.sealed_time_coef = data['SealedTimeCoef'] + ret.question_time_coef = data['QuestionTimeCoef'] + ret.blue_rose_time_coef = data['BlueRoseTimeCoef'] + ret.charm_time_coef = data['CharmTimeCoef'] + ret.burning_time_coef = data['BurningTimeCoef'] + ret.invalid_quake = data['InvalidQuake'] + ret.guard_coef = data['GuardCoef'] + ret.just_guard_time = data['JustGuardTime'] + ret.flip_hp = data['FlipHp'] + ret.down_hp = data['DownHp'] + ret.flip_recover_time = data['FlipRecoverTime'] + ret.down_recover_time = data['DownRecoverTime'] + ret.down_time = data['DownTime'] + ret.loss_damage = data['LossDamage'] + ret.loss_time = data['LossTime'] + ret.move_speed = data['MoveSpeed'] + ret.move_speed_angry = data['MoveSpeedAngry'] + ret.rot_speed = data['RotSpeed'] + ret.rot_speed_angry = data['RotSpeedAngry'] + ret.range_min = data['RangeMin'] + ret.range_max = data['RangeMax'] + ret.range_min_angry = data['RangeMinAngry'] + ret.range_max_angry = data['RangeMaxAngry'] + ret.back_step_speed = data['BackStepSpeed'] + ret.back_step_speed_angry = data['BackStepSpeedAngry'] + ret.back_step_cool_time = data['BackStepCoolTime'] + ret.monster_type = data['MonsterType'] + ret.monster_type_angry = data['MonsterTypeAngry'] + ret.vs_boss_type = data['VsBossType'] + ret.boss_camera_height = data['BossCameraHeight'] + ret.boss_camera_near_distance = data['BossCameraNearDistance'] + ret.boss_camera_far_distance = data['BossCameraFarDistance'] + ret.boss_camera_near_range = data['BossCameraNearRange'] + ret.boss_camera_far_range = data['BossCameraFarRange'] + ret.search_range = data['SearchRange'] + ret.search_angle = data['SearchAngle'] + ret.lost_range = data['LostRange'] + ret.home_range = data['HomeRange'] + ret.roar_id = data['RoarId'] + ret.auto_attack_range = data['AutoAttackRange'] + ret.auto_attack_interval = data['AutoAttackInterval'] + ret.find_priority = data['FindPriority'] + ret.find_range_min = data['FindRangeMin'] + ret.find_range_max = data['FindRangeMax'] + ret.leave_range = data['LeaveRange'] + ret.show_arrow = data['ShowArrow'] + ret.find_id = data['FindId'] + ret.leave_id = data['LeaveId'] + ret.defeat_id = data['DefeatId'] + ret.warfare_id = data['WarfareId'] + ret.angry_id = data['AngryId'] + ret.bad_state_bone = data['BadStateBone'] + ret.bad_state_offset_x = data['BadStateOffsetX'] + ret.bad_state_offset_y = data['BadStateOffsetY'] + ret.bad_state_offset_z = data['BadStateOffsetZ'] + ret.drop_offset_y = data['DropOffsetY'] + ret.col_drop_percentage = data['ColDropPercentage'] + ret.col_drop_amount = data['ColDropAmount'] + ret.vp_drop_percentage = data['VpDropPercentage'] + ret.vp_drop_amount = data['VpDropAmount'] + ret.enemy_kind_id = data['EnemyKindId'] + ret.defrag_match_league_point = data['DefragMatchLeaguePoint'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.unit_id) \ + + encode_str(self.name) \ + + encode_str(self.prefab) \ + + encode_str(self.animator) \ + + encode_str(self.collision) \ + + encode_str(self.radius) \ + + encode_int(self.child_unit_id) \ + + encode_str(self.stage_start_prefab) \ + + encode_str(self.stage_start_y) \ + + encode_str(self.stage_start_z) \ + + encode_str(self.stage_touch_y) \ + + encode_str(self.stage_touch_z) \ + + encode_byte(self.playable) \ + + encode_byte(self.monster) \ + + encode_byte(self.gimmick) \ + + encode_byte(self.anger) \ + + encode_byte(self.commander) \ + + encode_byte(self.isolated) \ + + encode_str(self.base_behaviour) \ + + encode_str(self.sound_behaviour) \ + + encode_int(self.weapon_type) \ + + encode_str(self.poison_time_coef) \ + + encode_str(self.paralysis_time_coef) \ + + encode_str(self.sealed_time_coef) \ + + encode_str(self.question_time_coef) \ + + encode_str(self.blue_rose_time_coef) \ + + encode_str(self.charm_time_coef) \ + + encode_str(self.burning_time_coef) \ + + encode_byte(self.invalid_quake) \ + + encode_str(self.guard_coef) \ + + encode_str(self.just_guard_time) \ + + encode_short(self.flip_hp) \ + + encode_short(self.down_hp) \ + + encode_str(self.flip_recover_time) \ + + encode_str(self.down_recover_time) \ + + encode_str(self.down_time) \ + + encode_short(self.loss_damage) \ + + encode_str(self.loss_time) \ + + encode_str(self.move_speed) \ + + encode_str(self.move_speed_angry) \ + + encode_str(self.rot_speed) \ + + encode_str(self.rot_speed_angry) \ + + encode_str(self.range_min) \ + + encode_str(self.range_max) \ + + encode_str(self.range_min_angry) \ + + encode_str(self.range_max_angry) \ + + encode_str(self.back_step_speed) \ + + encode_str(self.back_step_speed_angry) \ + + encode_str(self.back_step_cool_time) \ + + encode_short(self.monster_type) \ + + encode_short(self.monster_type_angry) \ + + encode_short(self.vs_boss_type) \ + + encode_str(self.boss_camera_height) \ + + encode_str(self.boss_camera_near_distance) \ + + encode_str(self.boss_camera_far_distance) \ + + encode_str(self.boss_camera_near_range) \ + + encode_str(self.boss_camera_far_range) \ + + encode_str(self.search_range) \ + + encode_str(self.search_angle) \ + + encode_str(self.lost_range) \ + + encode_str(self.home_range) \ + + encode_str(self.roar_id) \ + + encode_str(self.auto_attack_range) \ + + encode_str(self.auto_attack_interval) \ + + encode_short(self.find_priority) \ + + encode_short(self.find_range_min) \ + + encode_short(self.find_range_max) \ + + encode_short(self.leave_range) \ + + encode_byte(self.show_arrow) \ + + encode_str(self.find_id) \ + + encode_str(self.leave_id) \ + + encode_str(self.defeat_id) \ + + encode_str(self.warfare_id) \ + + encode_str(self.angry_id) \ + + encode_str(self.bad_state_bone) \ + + encode_str(self.bad_state_offset_x) \ + + encode_str(self.bad_state_offset_y) \ + + encode_str(self.bad_state_offset_z) \ + + encode_str(self.drop_offset_y) \ + + encode_str(self.col_drop_percentage) \ + + encode_short(self.col_drop_amount) \ + + encode_str(self.vp_drop_percentage) \ + + encode_short(self.vp_drop_amount) \ + + encode_int(self.enemy_kind_id) \ + + encode_int(self.defrag_match_league_point) + +class UnitGimmickData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.unit_gimmick_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.gimmick_type = decode_int(data, off) + off += INT_OFF + + self.range, new_off = decode_str(data, off) + off += new_off + + self.crystal_grade = decode_int(data, off) + off += INT_OFF + + self.col = decode_int(data, off) + off += INT_OFF + + self.skill_exp = decode_int(data, off) + off += INT_OFF + + self.heal_rate, new_off = decode_str(data, off) + off += new_off + + self.gimmick_attack_id = decode_short(data, off) + off += SHORT_OFF + + self.interval, new_off = decode_str(data, off) + off += new_off + + self.summon_count = decode_int(data, off) + off += INT_OFF + + self.effect, new_off = decode_str(data, off) + off += new_off + + self.effect_bone, new_off = decode_str(data, off) + off += new_off + + self.effect_x, new_off = decode_str(data, off) + off += new_off + + self.effect_y, new_off = decode_str(data, off) + off += new_off + + self.effect_z, new_off = decode_str(data, off) + off += new_off + + self.se, new_off = decode_str(data, off) + off += new_off + + self.ground_se, new_off = decode_str(data, off) + off += new_off + + self.sub_state_idle, new_off = decode_str(data, off) + off += new_off + + self.sub_state_reaction, new_off = decode_str(data, off) + off += new_off + + self.sub_state_active, new_off = decode_str(data, off) + off += new_off + + self.sub_state_break, new_off = decode_str(data, off) + off += new_off + + self.time_sub_state_break, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "UnitGimmickData": + ret = cls(b"\x00" * 99, 0) + ret.unit_gimmick_id = data['UnitGimmickId'] + ret.name = data['Name'] + ret.gimmick_type = data['GimmickType'] + ret.range = data['Range'] + ret.crystal_grade = data['CrystalGrade'] + ret.col = data['Col'] + ret.skill_exp = data['SkillExp'] + ret.heal_rate = data['HealRate'] + ret.gimmick_attack_id = data['GimmickAttackId'] + ret.interval = data['Interval'] + ret.summon_count = data['SummonCount'] + ret.effect = data['Effect'] + ret.effect_bone = data['EffectBone'] + ret.effect_x = data['EffectX'] + ret.effect_y = data['EffectY'] + ret.effect_z = data['EffectZ'] + ret.se = data['Se'] + ret.ground_se = data['GroundSe'] + ret.sub_state_idle = data['SubStateIdle'] + ret.sub_state_reaction = data['SubStateReaction'] + ret.sub_state_active = data['SubStateActive'] + ret.sub_state_break = data['SubStateBreak'] + ret.time_sub_state_break = data['TimeSubStateBreak'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.unit_gimmick_id) \ + + encode_str(self.name) \ + + encode_int(self.gimmick_type) \ + + encode_str(self.range) \ + + encode_int(self.crystal_grade) \ + + encode_int(self.col) \ + + encode_int(self.skill_exp) \ + + encode_str(self.heal_rate) \ + + encode_short(self.gimmick_attack_id) \ + + encode_str(self.interval) \ + + encode_int(self.summon_count) \ + + encode_str(self.effect) \ + + encode_str(self.effect_bone) \ + + encode_str(self.effect_x) \ + + encode_str(self.effect_y) \ + + encode_str(self.effect_z) \ + + encode_str(self.se) \ + + encode_str(self.ground_se) \ + + encode_str(self.sub_state_idle) \ + + encode_str(self.sub_state_reaction) \ + + encode_str(self.sub_state_active) \ + + encode_str(self.sub_state_break) \ + + encode_str(self.time_sub_state_break) + +class UnitCollisionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.unit_collision_id = decode_int(data, off) + off += INT_OFF + + self.keyword, new_off = decode_str(data, off) + off += new_off + + self.name, new_off = decode_str(data, off) + off += new_off + + self.bone, new_off = decode_str(data, off) + off += new_off + + self.usually = decode_byte(data, off) + off += BYTE_OFF + + self.angry = decode_byte(data, off) + off += BYTE_OFF + + self.type_index = decode_short(data, off) + off += SHORT_OFF + + self.begin_x, new_off = decode_str(data, off) + off += new_off + + self.begin_y, new_off = decode_str(data, off) + off += new_off + + self.begin_z, new_off = decode_str(data, off) + off += new_off + + self.end_x, new_off = decode_str(data, off) + off += new_off + + self.end_y, new_off = decode_str(data, off) + off += new_off + + self.end_z, new_off = decode_str(data, off) + off += new_off + + self.radius, new_off = decode_str(data, off) + off += new_off + + self.slash_coef, new_off = decode_str(data, off) + off += new_off + + self.strike_coef, new_off = decode_str(data, off) + off += new_off + + self.thrust_coef, new_off = decode_str(data, off) + off += new_off + + self.fire_coef, new_off = decode_str(data, off) + off += new_off + + self.water_coef, new_off = decode_str(data, off) + off += new_off + + self.air_coef, new_off = decode_str(data, off) + off += new_off + + self.earth_coef, new_off = decode_str(data, off) + off += new_off + + self.holy_coef, new_off = decode_str(data, off) + off += new_off + + self.dark_coef, new_off = decode_str(data, off) + off += new_off + + self.poison_coef, new_off = decode_str(data, off) + off += new_off + + self.paralysis_coef, new_off = decode_str(data, off) + off += new_off + + self.sealed_coef, new_off = decode_str(data, off) + off += new_off + + self.question_coef, new_off = decode_str(data, off) + off += new_off + + self.blue_rose_coef, new_off = decode_str(data, off) + off += new_off + + self.charm_coef, new_off = decode_str(data, off) + off += new_off + + self.burning_coef, new_off = decode_str(data, off) + off += new_off + + self.flip_coef, new_off = decode_str(data, off) + off += new_off + + self.down_coef, new_off = decode_str(data, off) + off += new_off + + self.knock_back_coef, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "UnitCollisionData": + ret = cls(b"\x00" * 99, 0) + ret.unit_collision_id = data['UnitCollisionId'] + ret.keyword = data['Keyword'] + ret.name = data['Name'] + ret.bone = data['Bone'] + ret.usually = data['Usually'] + ret.angry = data['Angry'] + ret.type_index = data['TypeIndex'] + ret.begin_x = data['BeginX'] + ret.begin_y = data['BeginY'] + ret.begin_z = data['BeginZ'] + ret.end_x = data['EndX'] + ret.end_y = data['EndY'] + ret.end_z = data['EndZ'] + ret.radius = data['Radius'] + ret.slash_coef = data['SlashCoef'] + ret.strike_coef = data['StrikeCoef'] + ret.thrust_coef = data['ThrustCoef'] + ret.fire_coef = data['FireCoef'] + ret.water_coef = data['WaterCoef'] + ret.air_coef = data['AirCoef'] + ret.earth_coef = data['EarthCoef'] + ret.holy_coef = data['HolyCoef'] + ret.dark_coef = data['DarkCoef'] + ret.poison_coef = data['PoisonCoef'] + ret.paralysis_coef = data['ParalysisCoef'] + ret.sealed_coef = data['SealedCoef'] + ret.question_coef = data['QuestionCoef'] + ret.blue_rose_coef = data['BlueRoseCoef'] + ret.charm_coef = data['CharmCoef'] + ret.burning_coef = data['BurningCoef'] + ret.flip_coef = data['FlipCoef'] + ret.down_coef = data['DownCoef'] + ret.knock_back_coef = data['KnockBackCoef'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.unit_collision_id) \ + + encode_str(self.keyword) \ + + encode_str(self.name) \ + + encode_str(self.bone) \ + + encode_byte(self.usually) \ + + encode_byte(self.angry) \ + + encode_short(self.type_index) \ + + encode_str(self.begin_x) \ + + encode_str(self.begin_y) \ + + encode_str(self.begin_z) \ + + encode_str(self.end_x) \ + + encode_str(self.end_y) \ + + encode_str(self.end_z) \ + + encode_str(self.radius) \ + + encode_str(self.slash_coef) \ + + encode_str(self.strike_coef) \ + + encode_str(self.thrust_coef) \ + + encode_str(self.fire_coef) \ + + encode_str(self.water_coef) \ + + encode_str(self.air_coef) \ + + encode_str(self.earth_coef) \ + + encode_str(self.holy_coef) \ + + encode_str(self.dark_coef) \ + + encode_str(self.poison_coef) \ + + encode_str(self.paralysis_coef) \ + + encode_str(self.sealed_coef) \ + + encode_str(self.question_coef) \ + + encode_str(self.blue_rose_coef) \ + + encode_str(self.charm_coef) \ + + encode_str(self.burning_coef) \ + + encode_str(self.flip_coef) \ + + encode_str(self.down_coef) \ + + encode_str(self.knock_back_coef) + +class UnitPowerData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.unit_power_id = decode_int(data, off) + off += INT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.lv = decode_int(data, off) + off += INT_OFF + + self.param_hp = decode_int(data, off) + off += INT_OFF + + self.param_str = decode_int(data, off) + off += INT_OFF + + self.param_vit = decode_int(data, off) + off += INT_OFF + + self.param_int = decode_int(data, off) + off += INT_OFF + + self.exp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "UnitPowerData": + ret = cls(b"\x00" * 99, 0) + ret.unit_power_id = data['UnitPowerId'] + ret.unit_id = data['UnitId'] + ret.lv = data['Lv'] + ret.param_hp = data['ParamHp'] + ret.param_str = data['ParamStr'] + ret.param_vit = data['ParamVit'] + ret.param_int = data['ParamInt'] + ret.exp = data['Exp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.unit_power_id) \ + + encode_int(self.unit_id) \ + + encode_int(self.lv) \ + + encode_int(self.param_hp) \ + + encode_int(self.param_str) \ + + encode_int(self.param_vit) \ + + encode_int(self.param_int) \ + + encode_int(self.exp) + +class GimmickAttackData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gimmick_attack_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.hit_enemy = decode_byte(data, off) + off += BYTE_OFF + + self.hit_friend = decode_byte(data, off) + off += BYTE_OFF + + self.attack_coef, new_off = decode_str(data, off) + off += new_off + + self.heal_coef, new_off = decode_str(data, off) + off += new_off + + self.flip_damage = decode_short(data, off) + off += SHORT_OFF + + self.down_damage = decode_short(data, off) + off += SHORT_OFF + + self.poison_incidence, new_off = decode_str(data, off) + off += new_off + + self.paralysis_incidence, new_off = decode_str(data, off) + off += new_off + + self.sealed_incidence, new_off = decode_str(data, off) + off += new_off + + self.question_incidence, new_off = decode_str(data, off) + off += new_off + + self.blue_rose_incidence, new_off = decode_str(data, off) + off += new_off + + self.charm_incidence, new_off = decode_str(data, off) + off += new_off + + self.burning_incidence, new_off = decode_str(data, off) + off += new_off + + self.knock_back, new_off = decode_str(data, off) + off += new_off + + self.knock_back_g, new_off = decode_str(data, off) + off += new_off + + self.forward_knock_back = decode_byte(data, off) + off += BYTE_OFF + + self.break_interval, new_off = decode_str(data, off) + off += new_off + + self.attack_type = decode_short(data, off) + off += SHORT_OFF + + self.physics_attr = decode_short(data, off) + off += SHORT_OFF + + self.magic_attr = decode_short(data, off) + off += SHORT_OFF + + self.hit_se, new_off = decode_str(data, off) + off += new_off + + self.weak_se, new_off = decode_str(data, off) + off += new_off + + self.resist_se, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GimmickAttackData": + ret = cls(b"\x00" * 99, 0) + ret.gimmick_attack_id = data['GimmickAttackId'] + ret.name = data['Name'] + ret.hit_enemy = data['HitEnemy'] + ret.hit_friend = data['HitFriend'] + ret.attack_coef = data['AttackCoef'] + ret.heal_coef = data['HealCoef'] + ret.flip_damage = data['FlipDamage'] + ret.down_damage = data['DownDamage'] + ret.poison_incidence = data['PoisonIncidence'] + ret.paralysis_incidence = data['ParalysisIncidence'] + ret.sealed_incidence = data['SealedIncidence'] + ret.question_incidence = data['QuestionIncidence'] + ret.blue_rose_incidence = data['BlueRoseIncidence'] + ret.charm_incidence = data['CharmIncidence'] + ret.burning_incidence = data['BurningIncidence'] + ret.knock_back = data['KnockBack'] + ret.knock_back_g = data['KnockBackG'] + ret.forward_knock_back = data['ForwardKnockBack'] + ret.break_interval = data['BreakInterval'] + ret.attack_type = data['AttackType'] + ret.physics_attr = data['PhysicsAttr'] + ret.magic_attr = data['MagicAttr'] + ret.hit_se = data['HitSe'] + ret.weak_se = data['WeakSe'] + ret.resist_se = data['ResistSe'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.gimmick_attack_id) \ + + encode_str(self.name) \ + + encode_byte(self.hit_enemy) \ + + encode_byte(self.hit_friend) \ + + encode_str(self.attack_coef) \ + + encode_str(self.heal_coef) \ + + encode_short(self.flip_damage) \ + + encode_short(self.down_damage) \ + + encode_str(self.poison_incidence) \ + + encode_str(self.paralysis_incidence) \ + + encode_str(self.sealed_incidence) \ + + encode_str(self.question_incidence) \ + + encode_str(self.blue_rose_incidence) \ + + encode_str(self.charm_incidence) \ + + encode_str(self.burning_incidence) \ + + encode_str(self.knock_back) \ + + encode_str(self.knock_back_g) \ + + encode_byte(self.forward_knock_back) \ + + encode_str(self.break_interval) \ + + encode_short(self.attack_type) \ + + encode_short(self.physics_attr) \ + + encode_short(self.magic_attr) \ + + encode_str(self.hit_se) \ + + encode_str(self.weak_se) \ + + encode_str(self.resist_se) + +class CharaAttackData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.chara_attack_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.attack_coef, new_off = decode_str(data, off) + off += new_off + + self.flip_damage = decode_short(data, off) + off += SHORT_OFF + + self.down_damage = decode_short(data, off) + off += SHORT_OFF + + self.poison_incidence, new_off = decode_str(data, off) + off += new_off + + self.paralysis_incidence, new_off = decode_str(data, off) + off += new_off + + self.sealed_incidence, new_off = decode_str(data, off) + off += new_off + + self.question_incidence, new_off = decode_str(data, off) + off += new_off + + self.blue_rose_incidence, new_off = decode_str(data, off) + off += new_off + + self.charm_incidence, new_off = decode_str(data, off) + off += new_off + + self.knock_back, new_off = decode_str(data, off) + off += new_off + + self.forward_knock_back = decode_byte(data, off) + off += BYTE_OFF + + self.break_interval, new_off = decode_str(data, off) + off += new_off + + self.weapon_slot = decode_short(data, off) + off += SHORT_OFF + + self.attack_type = decode_short(data, off) + off += SHORT_OFF + + self.physics_attr = decode_short(data, off) + off += SHORT_OFF + + self.magic_attr = decode_short(data, off) + off += SHORT_OFF + + self.spell_blast = decode_byte(data, off) + off += BYTE_OFF + + self.hit_se, new_off = decode_str(data, off) + off += new_off + + self.weak_se, new_off = decode_str(data, off) + off += new_off + + self.resist_se, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "CharaAttackData": + ret = cls(b"\x00" * 99, 0) + ret.chara_attack_id = data['CharaAttackId'] + ret.name = data['Name'] + ret.attack_coef = data['AttackCoef'] + ret.flip_damage = data['FlipDamage'] + ret.down_damage = data['DownDamage'] + ret.poison_incidence = data['PoisonIncidence'] + ret.paralysis_incidence = data['ParalysisIncidence'] + ret.sealed_incidence = data['SealedIncidence'] + ret.question_incidence = data['QuestionIncidence'] + ret.blue_rose_incidence = data['BlueRoseIncidence'] + ret.charm_incidence = data['CharmIncidence'] + ret.knock_back = data['KnockBack'] + ret.forward_knock_back = data['ForwardKnockBack'] + ret.break_interval = data['BreakInterval'] + ret.weapon_slot = data['WeaponSlot'] + ret.attack_type = data['AttackType'] + ret.physics_attr = data['PhysicsAttr'] + ret.magic_attr = data['MagicAttr'] + ret.spell_blast = data['SpellBlast'] + ret.hit_se = data['HitSe'] + ret.weak_se = data['WeakSe'] + ret.resist_se = data['ResistSe'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.chara_attack_id) \ + + encode_str(self.name) \ + + encode_str(self.attack_coef) \ + + encode_short(self.flip_damage) \ + + encode_short(self.down_damage) \ + + encode_str(self.poison_incidence) \ + + encode_str(self.paralysis_incidence) \ + + encode_str(self.sealed_incidence) \ + + encode_str(self.question_incidence) \ + + encode_str(self.blue_rose_incidence) \ + + encode_str(self.charm_incidence) \ + + encode_str(self.knock_back) \ + + encode_byte(self.forward_knock_back) \ + + encode_str(self.break_interval) \ + + encode_short(self.weapon_slot) \ + + encode_short(self.attack_type) \ + + encode_short(self.physics_attr) \ + + encode_short(self.magic_attr) \ + + encode_byte(self.spell_blast) \ + + encode_str(self.hit_se) \ + + encode_str(self.weak_se) \ + + encode_str(self.resist_se) + +class BossAttackData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.boss_attack_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.attack_coef, new_off = decode_str(data, off) + off += new_off + + self.flip_damage = decode_short(data, off) + off += SHORT_OFF + + self.down_damage = decode_short(data, off) + off += SHORT_OFF + + self.poison_incidence, new_off = decode_str(data, off) + off += new_off + + self.sealed_incidence, new_off = decode_str(data, off) + off += new_off + + self.question_incidence, new_off = decode_str(data, off) + off += new_off + + self.reduce_max_hp_incidence, new_off = decode_str(data, off) + off += new_off + + self.burning_incidence, new_off = decode_str(data, off) + off += new_off + + self.quake = decode_byte(data, off) + off += BYTE_OFF + + self.can_parry = decode_byte(data, off) + off += BYTE_OFF + + self.knock_back, new_off = decode_str(data, off) + off += new_off + + self.knock_back_g, new_off = decode_str(data, off) + off += new_off + + self.forward_knock_back = decode_byte(data, off) + off += BYTE_OFF + + self.attack_type = decode_short(data, off) + off += SHORT_OFF + + self.physics_attr = decode_short(data, off) + off += SHORT_OFF + + self.magic_attr = decode_short(data, off) + off += SHORT_OFF + + self.spell_blast = decode_byte(data, off) + off += BYTE_OFF + + self.hit_se, new_off = decode_str(data, off) + off += new_off + + self.weak_se, new_off = decode_str(data, off) + off += new_off + + self.resist_se, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BossAttackData": + ret = cls(b"\x00" * 99, 0) + ret.boss_attack_id = data['BossAttackId'] + ret.name = data['Name'] + ret.attack_coef = data['AttackCoef'] + ret.flip_damage = data['FlipDamage'] + ret.down_damage = data['DownDamage'] + ret.poison_incidence = data['PoisonIncidence'] + ret.sealed_incidence = data['SealedIncidence'] + ret.question_incidence = data['QuestionIncidence'] + ret.reduce_max_hp_incidence = data['ReduceMaxHpIncidence'] + ret.burning_incidence = data['BurningIncidence'] + ret.quake = data['Quake'] + ret.can_parry = data['CanParry'] + ret.knock_back = data['KnockBack'] + ret.knock_back_g = data['KnockBackG'] + ret.forward_knock_back = data['ForwardKnockBack'] + ret.attack_type = data['AttackType'] + ret.physics_attr = data['PhysicsAttr'] + ret.magic_attr = data['MagicAttr'] + ret.spell_blast = data['SpellBlast'] + ret.hit_se = data['HitSe'] + ret.weak_se = data['WeakSe'] + ret.resist_se = data['ResistSe'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.boss_attack_id) \ + + encode_str(self.name) \ + + encode_str(self.attack_coef) \ + + encode_short(self.flip_damage) \ + + encode_short(self.down_damage) \ + + encode_str(self.poison_incidence) \ + + encode_str(self.sealed_incidence) \ + + encode_str(self.question_incidence) \ + + encode_str(self.reduce_max_hp_incidence) \ + + encode_str(self.burning_incidence) \ + + encode_byte(self.quake) \ + + encode_byte(self.can_parry) \ + + encode_str(self.knock_back) \ + + encode_str(self.knock_back_g) \ + + encode_byte(self.forward_knock_back) \ + + encode_short(self.attack_type) \ + + encode_short(self.physics_attr) \ + + encode_short(self.magic_attr) \ + + encode_byte(self.spell_blast) \ + + encode_str(self.hit_se) \ + + encode_str(self.weak_se) \ + + encode_str(self.resist_se) + +class MonsterAttackData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.monster_attack_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.attack_coef, new_off = decode_str(data, off) + off += new_off + + self.flip_damage = decode_short(data, off) + off += SHORT_OFF + + self.down_damage = decode_short(data, off) + off += SHORT_OFF + + self.poison_incidence, new_off = decode_str(data, off) + off += new_off + + self.sealed_incidence, new_off = decode_str(data, off) + off += new_off + + self.question_incidence, new_off = decode_str(data, off) + off += new_off + + self.reduce_max_hp_incidence, new_off = decode_str(data, off) + off += new_off + + self.burning_incidence, new_off = decode_str(data, off) + off += new_off + + self.quake = decode_byte(data, off) + off += BYTE_OFF + + self.can_parry = decode_byte(data, off) + off += BYTE_OFF + + self.knock_back, new_off = decode_str(data, off) + off += new_off + + self.knock_back_g, new_off = decode_str(data, off) + off += new_off + + self.forward_knock_back = decode_byte(data, off) + off += BYTE_OFF + + self.attack_type = decode_short(data, off) + off += SHORT_OFF + + self.physics_attr = decode_short(data, off) + off += SHORT_OFF + + self.magic_attr = decode_short(data, off) + off += SHORT_OFF + + self.spell_blast = decode_byte(data, off) + off += BYTE_OFF + + self.hit_se, new_off = decode_str(data, off) + off += new_off + + self.weak_se, new_off = decode_str(data, off) + off += new_off + + self.resist_se, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "MonsterAttackData": + ret = cls(b"\x00" * 99, 0) + ret.monster_attack_id = data['MonsterAttackId'] + ret.name = data['Name'] + ret.attack_coef = data['AttackCoef'] + ret.flip_damage = data['FlipDamage'] + ret.down_damage = data['DownDamage'] + ret.poison_incidence = data['PoisonIncidence'] + ret.sealed_incidence = data['SealedIncidence'] + ret.question_incidence = data['QuestionIncidence'] + ret.reduce_max_hp_incidence = data['ReduceMaxHpIncidence'] + ret.burning_incidence = data['BurningIncidence'] + ret.quake = data['Quake'] + ret.can_parry = data['CanParry'] + ret.knock_back = data['KnockBack'] + ret.knock_back_g = data['KnockBackG'] + ret.forward_knock_back = data['ForwardKnockBack'] + ret.attack_type = data['AttackType'] + ret.physics_attr = data['PhysicsAttr'] + ret.magic_attr = data['MagicAttr'] + ret.spell_blast = data['SpellBlast'] + ret.hit_se = data['HitSe'] + ret.weak_se = data['WeakSe'] + ret.resist_se = data['ResistSe'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.monster_attack_id) \ + + encode_str(self.name) \ + + encode_str(self.attack_coef) \ + + encode_short(self.flip_damage) \ + + encode_short(self.down_damage) \ + + encode_str(self.poison_incidence) \ + + encode_str(self.sealed_incidence) \ + + encode_str(self.question_incidence) \ + + encode_str(self.reduce_max_hp_incidence) \ + + encode_str(self.burning_incidence) \ + + encode_byte(self.quake) \ + + encode_byte(self.can_parry) \ + + encode_str(self.knock_back) \ + + encode_str(self.knock_back_g) \ + + encode_byte(self.forward_knock_back) \ + + encode_short(self.attack_type) \ + + encode_short(self.physics_attr) \ + + encode_short(self.magic_attr) \ + + encode_byte(self.spell_blast) \ + + encode_str(self.hit_se) \ + + encode_str(self.weak_se) \ + + encode_str(self.resist_se) + +class MonsterActionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.monster_action_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.slot = decode_int(data, off) + off += INT_OFF + + self.usable_lv = decode_int(data, off) + off += INT_OFF + + self.use_count = decode_int(data, off) + off += INT_OFF + + self.not_angry = decode_byte(data, off) + off += BYTE_OFF + + self.is_angry = decode_byte(data, off) + off += BYTE_OFF + + self.can_sealed = decode_byte(data, off) + off += BYTE_OFF + + self.multi_play_only = decode_byte(data, off) + off += BYTE_OFF + + self.angle_min, new_off = decode_str(data, off) + off += new_off + + self.angle_max, new_off = decode_str(data, off) + off += new_off + + self.range_min, new_off = decode_str(data, off) + off += new_off + + self.range_max, new_off = decode_str(data, off) + off += new_off + + self.attack_time, new_off = decode_str(data, off) + off += new_off + + self.delay_time, new_off = decode_str(data, off) + off += new_off + + self.cool_time, new_off = decode_str(data, off) + off += new_off + + self.state, new_off = decode_str(data, off) + off += new_off + + self.sub_state, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "MonsterActionData": + ret = cls(b"\x00" * 99, 0) + ret.monster_action_id = data['MonsterActionId'] + ret.name = data['Name'] + ret.unit_id = data['UnitId'] + ret.slot = data['Slot'] + ret.usable_lv = data['UsableLv'] + ret.use_count = data['UseCount'] + ret.not_angry = data['NotAngry'] + ret.is_angry = data['IsAngry'] + ret.can_sealed = data['CanSealed'] + ret.multi_play_only = data['MultiPlayOnly'] + ret.angle_min = data['AngleMin'] + ret.angle_max = data['AngleMax'] + ret.range_min = data['RangeMin'] + ret.range_max = data['RangeMax'] + ret.attack_time = data['AttackTime'] + ret.delay_time = data['DelayTime'] + ret.cool_time = data['CoolTime'] + ret.state = data['State'] + ret.sub_state = data['SubState'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.monster_action_id) \ + + encode_str(self.name) \ + + encode_int(self.unit_id) \ + + encode_int(self.slot) \ + + encode_int(self.usable_lv) \ + + encode_int(self.use_count) \ + + encode_byte(self.not_angry) \ + + encode_byte(self.is_angry) \ + + encode_byte(self.can_sealed) \ + + encode_byte(self.multi_play_only) \ + + encode_str(self.angle_min) \ + + encode_str(self.angle_max) \ + + encode_str(self.range_min) \ + + encode_str(self.range_max) \ + + encode_str(self.attack_time) \ + + encode_str(self.delay_time) \ + + encode_str(self.cool_time) \ + + encode_str(self.state) \ + + encode_str(self.sub_state) + +class PropertyData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.property_id = decode_int(data, off) + off += INT_OFF + + self.property_target_type = decode_short(data, off) + off += SHORT_OFF + + self.property_name, new_off = decode_str(data, off) + off += new_off + + self.property_name_format, new_off = decode_str(data, off) + off += new_off + + self.property_type_id = decode_int(data, off) + off += INT_OFF + + self.value1_min = decode_int(data, off) + off += INT_OFF + + self.value1_max = decode_int(data, off) + off += INT_OFF + + self.value2_min = decode_int(data, off) + off += INT_OFF + + self.value2_max = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "PropertyData": + ret = cls(b"\x00" * 99, 0) + ret.property_id = data['PropertyId'] + ret.property_target_type = data['PropertyTargetType'] + ret.property_name = data['PropertyName'] + ret.property_name_format = data['PropertyNameFormat'] + ret.property_type_id = data['PropertyTypeId'] + ret.value1_min = data['Value1Min'] + ret.value1_max = data['Value1Max'] + ret.value2_min = data['Value2Min'] + ret.value2_max = data['Value2Max'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.property_id) \ + + encode_short(self.property_target_type) \ + + encode_str(self.property_name) \ + + encode_str(self.property_name_format) \ + + encode_int(self.property_type_id) \ + + encode_int(self.value1_min) \ + + encode_int(self.value1_max) \ + + encode_int(self.value2_min) \ + + encode_int(self.value2_max) + +class PropertyTableData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.property_table_id = decode_int(data, off) + off += INT_OFF + + self.property_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.property_id = decode_int(data, off) + off += INT_OFF + + self.value1_min = decode_int(data, off) + off += INT_OFF + + self.value1_max = decode_int(data, off) + off += INT_OFF + + self.value2_min = decode_int(data, off) + off += INT_OFF + + self.value2_max = decode_int(data, off) + off += INT_OFF + + self.rate = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "PropertyTableData": + ret = cls(b"\x00" * 99, 0) + ret.property_table_id = data['PropertyTableId'] + ret.property_table_sub_id = data['PropertyTableSubId'] + ret.property_id = data['PropertyId'] + ret.value1_min = data['Value1Min'] + ret.value1_max = data['Value1Max'] + ret.value2_min = data['Value2Min'] + ret.value2_max = data['Value2Max'] + ret.rate = data['Rate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.property_table_id) \ + + encode_int(self.property_table_sub_id) \ + + encode_int(self.property_id) \ + + encode_int(self.value1_min) \ + + encode_int(self.value1_max) \ + + encode_int(self.value2_min) \ + + encode_int(self.value2_max) \ + + encode_int(self.rate) + +class PropertyTypeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.property_type_id = decode_int(data, off) + off += INT_OFF + + self.property_type_name, new_off = decode_str(data, off) + off += new_off + + self.physics_attr = decode_int(data, off) + off += INT_OFF + + self.magic_attr = decode_int(data, off) + off += INT_OFF + + self.permission_same_id_flag = decode_byte(data, off) + off += BYTE_OFF + + self.auto_equip_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "PropertyTypeData": + ret = cls(b"\x00" * 99, 0) + ret.property_type_id = data['PropertyTypeId'] + ret.property_type_name = data['PropertyTypeName'] + ret.physics_attr = data['PhysicsAttr'] + ret.magic_attr = data['MagicAttr'] + ret.permission_same_id_flag = data['PermissionSameIdFlag'] + ret.auto_equip_flag = data['AutoEquipFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.property_type_id) \ + + encode_str(self.property_type_name) \ + + encode_int(self.physics_attr) \ + + encode_int(self.magic_attr) \ + + encode_byte(self.permission_same_id_flag) \ + + encode_byte(self.auto_equip_flag) + +class SkillData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.skill_id = decode_short(data, off) + off += SHORT_OFF + + self.weapon_type_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.attack = decode_byte(data, off) + off += BYTE_OFF + + self.passive = decode_byte(data, off) + off += BYTE_OFF + + self.pet = decode_byte(data, off) + off += BYTE_OFF + + self.level = decode_short(data, off) + off += SHORT_OFF + + self.skill_condition = decode_short(data, off) + off += SHORT_OFF + + self.cool_time, new_off = decode_str(data, off) + off += new_off + + self.sword_color_r, new_off = decode_str(data, off) + off += new_off + + self.sword_color_g, new_off = decode_str(data, off) + off += new_off + + self.sword_color_b, new_off = decode_str(data, off) + off += new_off + + self.motion_index = decode_short(data, off) + off += SHORT_OFF + + self.state, new_off = decode_str(data, off) + off += new_off + + self.sub_state, new_off = decode_str(data, off) + off += new_off + + self.skill_icon, new_off = decode_str(data, off) + off += new_off + + self.friend_skill_icon, new_off = decode_str(data, off) + off += new_off + + self.info_text, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SkillData": + ret = cls(b"\x00" * 99, 0) + ret.skill_id = data['SkillId'] + ret.weapon_type_id = data['WeaponTypeId'] + ret.name = data['Name'] + ret.attack = data['Attack'] + ret.passive = data['Passive'] + ret.pet = data['Pet'] + ret.level = data['Level'] + ret.skill_condition = data['SkillCondition'] + ret.cool_time = data['CoolTime'] + ret.sword_color_r = data['SwordColorR'] + ret.sword_color_g = data['SwordColorG'] + ret.sword_color_b = data['SwordColorB'] + ret.motion_index = data['MotionIndex'] + ret.state = data['State'] + ret.sub_state = data['SubState'] + ret.skill_icon = data['SkillIcon'] + ret.friend_skill_icon = data['FriendSkillIcon'] + ret.info_text = data['InfoText'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.skill_id) \ + + encode_short(self.weapon_type_id) \ + + encode_str(self.name) \ + + encode_byte(self.attack) \ + + encode_byte(self.passive) \ + + encode_byte(self.pet) \ + + encode_short(self.level) \ + + encode_short(self.skill_condition) \ + + encode_str(self.cool_time) \ + + encode_str(self.sword_color_r) \ + + encode_str(self.sword_color_g) \ + + encode_str(self.sword_color_b) \ + + encode_short(self.motion_index) \ + + encode_str(self.state) \ + + encode_str(self.sub_state) \ + + encode_str(self.skill_icon) \ + + encode_str(self.friend_skill_icon) \ + + encode_str(self.info_text) + +class SkillTableData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.skill_table_id = decode_int(data, off) + off += INT_OFF + + self.skill_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.level = decode_int(data, off) + off += INT_OFF + + self.awakening_id = decode_int(data, off) + off += INT_OFF + + self.skill_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SkillTableData": + ret = cls(b"\x00" * 99, 0) + ret.skill_table_id = data['SkillTableId'] + ret.skill_table_sub_id = data['SkillTableSubId'] + ret.level = data['Level'] + ret.awakening_id = data['AwakeningId'] + ret.skill_id = data['SkillId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.skill_table_id) \ + + encode_int(self.skill_table_sub_id) \ + + encode_int(self.level) \ + + encode_int(self.awakening_id) \ + + encode_int(self.skill_id) + +class SkillLevelData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.skill_level_id = decode_short(data, off) + off += SHORT_OFF + + self.exp = decode_int(data, off) + off += INT_OFF + + self.exp_total = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SkillLevelData": + ret = cls(b"\x00" * 99, 0) + ret.skill_level_id = data['SkillLevelId'] + ret.exp = data['Exp'] + ret.exp_total = data['ExpTotal'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.skill_level_id) \ + + encode_int(self.exp) \ + + encode_int(self.exp_total) + +class AwakeningData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.awakening_id = decode_short(data, off) + off += SHORT_OFF + + self.total_exp = decode_int(data, off) + off += INT_OFF + + self.bonus_hero_log, new_off = decode_str(data, off) + off += new_off + + self.bonus_weapon, new_off = decode_str(data, off) + off += new_off + + self.bonus_armor, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "AwakeningData": + ret = cls(b"\x00" * 99, 0) + ret.awakening_id = data['AwakeningId'] + ret.total_exp = data['TotalExp'] + ret.bonus_hero_log = data['BonusHeroLog'] + ret.bonus_weapon = data['BonusWeapon'] + ret.bonus_armor = data['BonusArmor'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.awakening_id) \ + + encode_int(self.total_exp) \ + + encode_str(self.bonus_hero_log) \ + + encode_str(self.bonus_weapon) \ + + encode_str(self.bonus_armor) + +class SynchroSkillData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.synchro_skill_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.name_in_skill_cutin, new_off = decode_str(data, off) + off += new_off + + self.sub_state, new_off = decode_str(data, off) + off += new_off + + self.chara_id = decode_short(data, off) + off += SHORT_OFF + + self.icon_file_path, new_off = decode_str(data, off) + off += new_off + + self.info_text, new_off = decode_str(data, off) + off += new_off + + self.se_name, new_off = decode_str(data, off) + off += new_off + + self.voice_id, new_off = decode_str(data, off) + off += new_off + + self.delay_time = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SynchroSkillData": + ret = cls(b"\x00" * 99, 0) + ret.synchro_skill_id = data['SynchroSkillId'] + ret.name = data['Name'] + ret.name_in_skill_cutin = data['NameInSkillCutin'] + ret.sub_state = data['SubState'] + ret.chara_id = data['CharaId'] + ret.icon_file_path = data['IconFilePath'] + ret.info_text = data['InfoText'] + ret.se_name = data['SeName'] + ret.voice_id = data['VoiceId'] + ret.delay_time = data['DelayTime'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.synchro_skill_id) \ + + encode_str(self.name) \ + + encode_str(self.name_in_skill_cutin) \ + + encode_str(self.sub_state) \ + + encode_short(self.chara_id) \ + + encode_str(self.icon_file_path) \ + + encode_str(self.info_text) \ + + encode_str(self.se_name) \ + + encode_str(self.voice_id) \ + + encode_int(self.delay_time) + +class SoundSkillCutInVoiceData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.sound_skill_cut_in_voice_id = decode_short(data, off) + off += SHORT_OFF + + self.skill_id = decode_int(data, off) + off += INT_OFF + + self.chara_id = decode_int(data, off) + off += INT_OFF + + self.record_id, new_off = decode_str(data, off) + off += new_off + + self.que_name, new_off = decode_str(data, off) + off += new_off + + self.delay_time = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SoundSkillCutInVoiceData": + ret = cls(b"\x00" * 99, 0) + ret.sound_skill_cut_in_voice_id = data['SoundSkillCutInVoiceId'] + ret.skill_id = data['SkillId'] + ret.chara_id = data['CharaId'] + ret.record_id = data['RecordId'] + ret.que_name = data['QueName'] + ret.delay_time = data['DelayTime'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.sound_skill_cut_in_voice_id) \ + + encode_int(self.skill_id) \ + + encode_int(self.chara_id) \ + + encode_str(self.record_id) \ + + encode_str(self.que_name) \ + + encode_int(self.delay_time) + +class QuestSceneData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_scene_id = decode_short(data, off) + off += SHORT_OFF + + self.sort_no = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.map_data, new_off = decode_str(data, off) + off += new_off + + self.unit_data, new_off = decode_str(data, off) + off += new_off + + self.demo_map, new_off = decode_str(data, off) + off += new_off + + self.bgm_basic, new_off = decode_str(data, off) + off += new_off + + self.bgm_boss, new_off = decode_str(data, off) + off += new_off + + self.tutorial = decode_byte(data, off) + off += BYTE_OFF + + self.chara_comment_id_0 = decode_int(data, off) + off += INT_OFF + + self.chara_comment_id_1 = decode_int(data, off) + off += INT_OFF + + self.chara_comment_id_2 = decode_int(data, off) + off += INT_OFF + + self.chara_comment_id_3 = decode_int(data, off) + off += INT_OFF + + self.chara_comment_id_4 = decode_int(data, off) + off += INT_OFF + + self.col_rate, new_off = decode_str(data, off) + off += new_off + + self.limit_default = decode_int(data, off) + off += INT_OFF + + self.limit_time_dec, new_off = decode_str(data, off) + off += new_off + + self.limit_chara_dec, new_off = decode_str(data, off) + off += new_off + + self.limit_resurrection = decode_int(data, off) + off += INT_OFF + + self.mission_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.mission_enemy_lv_limit = decode_short(data, off) + off += SHORT_OFF + + self.reward_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.player_trace_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.success_player_exp = decode_int(data, off) + off += INT_OFF + + self.failed_player_exp = decode_int(data, off) + off += INT_OFF + + self.greed_spawn_wait_count = decode_int(data, off) + off += INT_OFF + + self.honey_spawn_wait_count = decode_int(data, off) + off += INT_OFF + + self.menu_display_enemy_set_id = decode_int(data, off) + off += INT_OFF + + self.stage_filepath, new_off = decode_str(data, off) + off += new_off + + self.rarity_up_chance_rate, new_off = decode_str(data, off) + off += new_off + + self.pair_mob_hp_rate, new_off = decode_str(data, off) + off += new_off + + self.pair_mob_atk_rate, new_off = decode_str(data, off) + off += new_off + + self.pair_leader_hp_rate, new_off = decode_str(data, off) + off += new_off + + self.pair_leader_atk_rate, new_off = decode_str(data, off) + off += new_off + + self.pair_boss_hp_rate, new_off = decode_str(data, off) + off += new_off + + self.pair_boss_atk_rate, new_off = decode_str(data, off) + off += new_off + + self.pair_exp_rate, new_off = decode_str(data, off) + off += new_off + + self.trio_mob_hp_rate, new_off = decode_str(data, off) + off += new_off + + self.trio_mob_atk_rate, new_off = decode_str(data, off) + off += new_off + + self.trio_leader_hp_rate, new_off = decode_str(data, off) + off += new_off + + self.trio_leader_atk_rate, new_off = decode_str(data, off) + off += new_off + + self.trio_boss_hp_rate, new_off = decode_str(data, off) + off += new_off + + self.trio_boss_atk_rate, new_off = decode_str(data, off) + off += new_off + + self.trio_exp_rate, new_off = decode_str(data, off) + off += new_off + + self.single_reward_vp = decode_int(data, off) + off += INT_OFF + + self.pair_reward_vp = decode_int(data, off) + off += INT_OFF + + self.trio_reward_vp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestSceneData": + ret = cls(b"\x00" * 99, 0) + ret.quest_scene_id = data['QuestSceneId'] + ret.sort_no = data['SortNo'] + ret.name = data['Name'] + ret.map_data = data['MapData'] + ret.unit_data = data['UnitData'] + ret.demo_map = data['DemoMap'] + ret.bgm_basic = data['BgmBasic'] + ret.bgm_boss = data['BgmBoss'] + ret.tutorial = data['Tutorial'] + ret.chara_comment_id_0 = data['CharaCommentId0'] + ret.chara_comment_id_1 = data['CharaCommentId1'] + ret.chara_comment_id_2 = data['CharaCommentId2'] + ret.chara_comment_id_3 = data['CharaCommentId3'] + ret.chara_comment_id_4 = data['CharaCommentId4'] + ret.col_rate = data['ColRate'] + ret.limit_default = data['LimitDefault'] + ret.limit_time_dec = data['LimitTimeDec'] + ret.limit_chara_dec = data['LimitCharaDec'] + ret.limit_resurrection = data['LimitResurrection'] + ret.mission_table_sub_id = data['MissionTableSubId'] + ret.mission_enemy_lv_limit = data['MissionEnemyLvLimit'] + ret.reward_table_sub_id = data['RewardTableSubId'] + ret.player_trace_table_sub_id = data['PlayerTraceTableSubId'] + ret.success_player_exp = data['SuccessPlayerExp'] + ret.failed_player_exp = data['FailedPlayerExp'] + ret.greed_spawn_wait_count = data['GreedSpawnWaitCount'] + ret.honey_spawn_wait_count = data['HoneySpawnWaitCount'] + ret.menu_display_enemy_set_id = data['MenuDisplayEnemySetId'] + ret.stage_filepath = data['StageFilepath'] + ret.rarity_up_chance_rate = data['RarityUpChanceRate'] + ret.pair_mob_hp_rate = data['PairMobHpRate'] + ret.pair_mob_atk_rate = data['PairMobAtkRate'] + ret.pair_leader_hp_rate = data['PairLeaderHpRate'] + ret.pair_leader_atk_rate = data['PairLeaderAtkRate'] + ret.pair_boss_hp_rate = data['PairBossHpRate'] + ret.pair_boss_atk_rate = data['PairBossAtkRate'] + ret.pair_exp_rate = data['PairExpRate'] + ret.trio_mob_hp_rate = data['TrioMobHpRate'] + ret.trio_mob_atk_rate = data['TrioMobAtkRate'] + ret.trio_leader_hp_rate = data['TrioLeaderHpRate'] + ret.trio_leader_atk_rate = data['TrioLeaderAtkRate'] + ret.trio_boss_hp_rate = data['TrioBossHpRate'] + ret.trio_boss_atk_rate = data['TrioBossAtkRate'] + ret.trio_exp_rate = data['TrioExpRate'] + ret.single_reward_vp = data['SingleRewardVp'] + ret.pair_reward_vp = data['PairRewardVp'] + ret.trio_reward_vp = data['TrioRewardVp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.quest_scene_id) \ + + encode_short(self.sort_no) \ + + encode_str(self.name) \ + + encode_str(self.map_data) \ + + encode_str(self.unit_data) \ + + encode_str(self.demo_map) \ + + encode_str(self.bgm_basic) \ + + encode_str(self.bgm_boss) \ + + encode_byte(self.tutorial) \ + + encode_int(self.chara_comment_id_0) \ + + encode_int(self.chara_comment_id_1) \ + + encode_int(self.chara_comment_id_2) \ + + encode_int(self.chara_comment_id_3) \ + + encode_int(self.chara_comment_id_4) \ + + encode_str(self.col_rate) \ + + encode_int(self.limit_default) \ + + encode_str(self.limit_time_dec) \ + + encode_str(self.limit_chara_dec) \ + + encode_int(self.limit_resurrection) \ + + encode_int(self.mission_table_sub_id) \ + + encode_short(self.mission_enemy_lv_limit) \ + + encode_int(self.reward_table_sub_id) \ + + encode_int(self.player_trace_table_sub_id) \ + + encode_int(self.success_player_exp) \ + + encode_int(self.failed_player_exp) \ + + encode_int(self.greed_spawn_wait_count) \ + + encode_int(self.honey_spawn_wait_count) \ + + encode_int(self.menu_display_enemy_set_id) \ + + encode_str(self.stage_filepath) \ + + encode_str(self.rarity_up_chance_rate) \ + + encode_str(self.pair_mob_hp_rate) \ + + encode_str(self.pair_mob_atk_rate) \ + + encode_str(self.pair_leader_hp_rate) \ + + encode_str(self.pair_leader_atk_rate) \ + + encode_str(self.pair_boss_hp_rate) \ + + encode_str(self.pair_boss_atk_rate) \ + + encode_str(self.pair_exp_rate) \ + + encode_str(self.trio_mob_hp_rate) \ + + encode_str(self.trio_mob_atk_rate) \ + + encode_str(self.trio_leader_hp_rate) \ + + encode_str(self.trio_leader_atk_rate) \ + + encode_str(self.trio_boss_hp_rate) \ + + encode_str(self.trio_boss_atk_rate) \ + + encode_str(self.trio_exp_rate) \ + + encode_int(self.single_reward_vp) \ + + encode_int(self.pair_reward_vp) \ + + encode_int(self.trio_reward_vp) + +class QuestExistUnitData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_exist_unit_id = decode_int(data, off) + off += INT_OFF + + self.quest_scene_id = decode_int(data, off) + off += INT_OFF + + self.enemy_kind_id = decode_int(data, off) + off += INT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestExistUnitData": + ret = cls(b"\x00" * 99, 0) + ret.quest_exist_unit_id = data['QuestExistUnitId'] + ret.quest_scene_id = data['QuestSceneId'] + ret.enemy_kind_id = data['EnemyKindId'] + ret.unit_id = data['UnitId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.quest_exist_unit_id) \ + + encode_int(self.quest_scene_id) \ + + encode_int(self.enemy_kind_id) \ + + encode_int(self.unit_id) + +class QuestEpisodeAppendRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_episode_append_reward_id = decode_int(data, off) + off += INT_OFF + + self.quest_scene_id = decode_int(data, off) + off += INT_OFF + + self.episode_append_id = decode_int(data, off) + off += INT_OFF + + self.episode_append_num = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestEpisodeAppendRewardData": + ret = cls(b"\x00" * 99, 0) + ret.quest_episode_append_reward_id = data['QuestEpisodeAppendRewardId'] + ret.quest_scene_id = data['QuestSceneId'] + ret.episode_append_id = data['EpisodeAppendId'] + ret.episode_append_num = data['EpisodeAppendNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.quest_episode_append_reward_id) \ + + encode_int(self.quest_scene_id) \ + + encode_int(self.episode_append_id) \ + + encode_int(self.episode_append_num) + +class SideQuestData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.side_quest_id = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self.episode_num = decode_int(data, off) + off += INT_OFF + + self.ex_bonus_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.quest_scene_id = decode_int(data, off) + off += INT_OFF + + self.recommend_lv = decode_int(data, off) + off += INT_OFF + + self.comment_summary, new_off = decode_str(data, off) + off += new_off + + self.comment_introduction, new_off = decode_str(data, off) + off += new_off + + self.start_adv_name, new_off = decode_str(data, off) + off += new_off + + self.end_adv_name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SideQuestData": + ret = cls(b"\x00" * 99, 0) + ret.side_quest_id = data['SideQuestId'] + ret.display_name = data['DisplayName'] + ret.episode_num = data['EpisodeNum'] + ret.ex_bonus_table_sub_id = data['ExBonusTableSubId'] + ret.quest_scene_id = data['QuestSceneId'] + ret.recommend_lv = data['RecommendLv'] + ret.comment_summary = data['CommentSummary'] + ret.comment_introduction = data['CommentIntroduction'] + ret.start_adv_name = data['StartAdvName'] + ret.end_adv_name = data['EndAdvName'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.side_quest_id) \ + + encode_str(self.display_name) \ + + encode_int(self.episode_num) \ + + encode_int(self.ex_bonus_table_sub_id) \ + + encode_int(self.quest_scene_id) \ + + encode_int(self.recommend_lv) \ + + encode_str(self.comment_summary) \ + + encode_str(self.comment_introduction) \ + + encode_str(self.start_adv_name) \ + + encode_str(self.end_adv_name) + +class EpisodeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.episode_id = decode_int(data, off) + off += INT_OFF + + self.episode_chapter_id = decode_int(data, off) + off += INT_OFF + + self.release_episode_id = decode_int(data, off) + off += INT_OFF + + self.episode_num = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.comment_summary, new_off = decode_str(data, off) + off += new_off + + self.comment_introduction, new_off = decode_str(data, off) + off += new_off + + self.recommend_lv = decode_int(data, off) + off += INT_OFF + + self.ex_bonus_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.quest_scene_id = decode_int(data, off) + off += INT_OFF + + self.start_adv_name, new_off = decode_str(data, off) + off += new_off + + self.end_adv_name, new_off = decode_str(data, off) + off += new_off + + self.unlock_still_id = decode_int(data, off) + off += INT_OFF + + self.required_release_episode_append_id = decode_int(data, off) + off += INT_OFF + + self.required_release_episode_append_num = decode_short(data, off) + off += SHORT_OFF + + self.release_reward_vp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EpisodeData": + ret = cls(b"\x00" * 99, 0) + ret.episode_id = data['EpisodeId'] + ret.episode_chapter_id = data['EpisodeChapterId'] + ret.release_episode_id = data['ReleaseEpisodeId'] + ret.episode_num = data['EpisodeNum'] + ret.title = data['Title'] + ret.comment_summary = data['CommentSummary'] + ret.comment_introduction = data['CommentIntroduction'] + ret.recommend_lv = data['RecommendLv'] + ret.ex_bonus_table_sub_id = data['ExBonusTableSubId'] + ret.quest_scene_id = data['QuestSceneId'] + ret.start_adv_name = data['StartAdvName'] + ret.end_adv_name = data['EndAdvName'] + ret.unlock_still_id = data['UnlockStillId'] + ret.required_release_episode_append_id = data['RequiredReleaseEpisodeAppendId'] + ret.required_release_episode_append_num = data['RequiredReleaseEpisodeAppendNum'] + ret.release_reward_vp = data['ReleaseRewardVp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.episode_id) \ + + encode_int(self.episode_chapter_id) \ + + encode_int(self.release_episode_id) \ + + encode_int(self.episode_num) \ + + encode_str(self.title) \ + + encode_str(self.comment_summary) \ + + encode_str(self.comment_introduction) \ + + encode_int(self.recommend_lv) \ + + encode_int(self.ex_bonus_table_sub_id) \ + + encode_int(self.quest_scene_id) \ + + encode_str(self.start_adv_name) \ + + encode_str(self.end_adv_name) \ + + encode_int(self.unlock_still_id) \ + + encode_int(self.required_release_episode_append_id) \ + + encode_short(self.required_release_episode_append_num) \ + + encode_int(self.release_reward_vp) + +class EpisodeChapterData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.episode_chapter_id = decode_int(data, off) + off += INT_OFF + + self.episode_part_id = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EpisodeChapterData": + ret = cls(b"\x00" * 99, 0) + ret.episode_chapter_id = data['EpisodeChapterId'] + ret.episode_part_id = data['EpisodePartId'] + ret.display_name = data['DisplayName'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.episode_chapter_id) \ + + encode_int(self.episode_part_id) \ + + encode_str(self.display_name) + +class EpisodePartData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.episode_part_id = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EpisodePartData": + ret = cls(b"\x00" * 99, 0) + ret.episode_part_id = data['EpisodePartId'] + ret.display_name = data['DisplayName'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.episode_part_id) \ + + encode_str(self.display_name) + +class TrialTowerData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.trial_tower_id = decode_short(data, off) + off += SHORT_OFF + + self.release_trial_tower_id = decode_int(data, off) + off += INT_OFF + + self.ex_bonus_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.quest_scene_id = decode_int(data, off) + off += INT_OFF + + self.recommend_lv = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TrialTowerData": + ret = cls(b"\x00" * 99, 0) + ret.trial_tower_id = data['TrialTowerId'] + ret.release_trial_tower_id = data['ReleaseTrialTowerId'] + ret.ex_bonus_table_sub_id = data['ExBonusTableSubId'] + ret.quest_scene_id = data['QuestSceneId'] + ret.recommend_lv = data['RecommendLv'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.trial_tower_id) \ + + encode_int(self.release_trial_tower_id) \ + + encode_int(self.ex_bonus_table_sub_id) \ + + encode_int(self.quest_scene_id) \ + + encode_int(self.recommend_lv) + +class ExTowerData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ex_tower_id = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.release_trial_tower_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ExTowerData": + ret = cls(b"\x00" * 99, 0) + ret.ex_tower_id = data['ExTowerId'] + ret.title = data['Title'] + ret.release_trial_tower_id = data['ReleaseTrialTowerId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.ex_tower_id) \ + + encode_str(self.title) \ + + encode_int(self.release_trial_tower_id) + +class ExTowerQuestData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ex_tower_quest_id = decode_int(data, off) + off += INT_OFF + + self.ex_tower_id = decode_int(data, off) + off += INT_OFF + + self.release_ex_tower_quest_id = decode_int(data, off) + off += INT_OFF + + self.level_num = decode_short(data, off) + off += SHORT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.ex_bonus_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.quest_scene_id = decode_int(data, off) + off += INT_OFF + + self.recommend_lv = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ExTowerQuestData": + ret = cls(b"\x00" * 99, 0) + ret.ex_tower_quest_id = data['ExTowerQuestId'] + ret.ex_tower_id = data['ExTowerId'] + ret.release_ex_tower_quest_id = data['ReleaseExTowerQuestId'] + ret.level_num = data['LevelNum'] + ret.title = data['Title'] + ret.ex_bonus_table_sub_id = data['ExBonusTableSubId'] + ret.quest_scene_id = data['QuestSceneId'] + ret.recommend_lv = data['RecommendLv'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.ex_tower_quest_id) \ + + encode_int(self.ex_tower_id) \ + + encode_int(self.release_ex_tower_quest_id) \ + + encode_short(self.level_num) \ + + encode_str(self.title) \ + + encode_int(self.ex_bonus_table_sub_id) \ + + encode_int(self.quest_scene_id) \ + + encode_int(self.recommend_lv) + +class MenuDisplayEnemyData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.menu_display_enemy_id = decode_short(data, off) + off += SHORT_OFF + + self.menu_display_enemy_set_id = decode_int(data, off) + off += INT_OFF + + self.boss_flag = decode_byte(data, off) + off += BYTE_OFF + + self.enemy_kind_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "MenuDisplayEnemyData": + ret = cls(b"\x00" * 99, 0) + ret.menu_display_enemy_id = data['MenuDisplayEnemyId'] + ret.menu_display_enemy_set_id = data['MenuDisplayEnemySetId'] + ret.boss_flag = data['BossFlag'] + ret.enemy_kind_id = data['EnemyKindId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.menu_display_enemy_id) \ + + encode_int(self.menu_display_enemy_set_id) \ + + encode_byte(self.boss_flag) \ + + encode_int(self.enemy_kind_id) + +class MissionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.mission_id = decode_short(data, off) + off += SHORT_OFF + + self.mission_difficulty_id = decode_int(data, off) + off += INT_OFF + + self.mission_name, new_off = decode_str(data, off) + off += new_off + + self.time_limit = decode_int(data, off) + off += INT_OFF + + self.condition = decode_short(data, off) + off += SHORT_OFF + + self.enemy_set_id = decode_int(data, off) + off += INT_OFF + + self.value1 = decode_int(data, off) + off += INT_OFF + + self.value2 = decode_int(data, off) + off += INT_OFF + + self.enemy_hide = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "MissionData": + ret = cls(b"\x00" * 99, 0) + ret.mission_id = data['MissionId'] + ret.mission_difficulty_id = data['MissionDifficultyId'] + ret.mission_name = data['MissionName'] + ret.time_limit = data['TimeLimit'] + ret.condition = data['Condition'] + ret.enemy_set_id = data['EnemySetId'] + ret.value1 = data['Value1'] + ret.value2 = data['Value2'] + ret.enemy_hide = data['EnemyHide'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.mission_id) \ + + encode_int(self.mission_difficulty_id) \ + + encode_str(self.mission_name) \ + + encode_int(self.time_limit) \ + + encode_short(self.condition) \ + + encode_int(self.enemy_set_id) \ + + encode_int(self.value1) \ + + encode_int(self.value2) \ + + encode_byte(self.enemy_hide) + +class MissionTableData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.mission_table_id = decode_int(data, off) + off += INT_OFF + + self.mission_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.mission_id = decode_int(data, off) + off += INT_OFF + + self.rate = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "MissionTableData": + ret = cls(b"\x00" * 99, 0) + ret.mission_table_id = data['MissionTableId'] + ret.mission_table_sub_id = data['MissionTableSubId'] + ret.mission_id = data['MissionId'] + ret.rate = data['Rate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.mission_table_id) \ + + encode_int(self.mission_table_sub_id) \ + + encode_int(self.mission_id) \ + + encode_int(self.rate) + +class MissionDifficultyData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.mission_difficulty_id = decode_short(data, off) + off += SHORT_OFF + + self.skill_exp = decode_int(data, off) + off += INT_OFF + + self.unanalyzed_log_exp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "MissionDifficultyData": + ret = cls(b"\x00" * 99, 0) + ret.mission_difficulty_id = data['MissionDifficultyId'] + ret.skill_exp = data['SkillExp'] + ret.unanalyzed_log_exp = data['UnanalyzedLogExp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.mission_difficulty_id) \ + + encode_int(self.skill_exp) \ + + encode_int(self.unanalyzed_log_exp) + +class BattleCameraData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.battle_camera_id = decode_short(data, off) + off += SHORT_OFF + + self.offset_x, new_off = decode_str(data, off) + off += new_off + + self.offset_y, new_off = decode_str(data, off) + off += new_off + + self.offset_z, new_off = decode_str(data, off) + off += new_off + + self.rot_h, new_off = decode_str(data, off) + off += new_off + + self.rot_v, new_off = decode_str(data, off) + off += new_off + + self.distance, new_off = decode_str(data, off) + off += new_off + + self.near, new_off = decode_str(data, off) + off += new_off + + self.far, new_off = decode_str(data, off) + off += new_off + + self.fov, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BattleCameraData": + ret = cls(b"\x00" * 99, 0) + ret.battle_camera_id = data['BattleCameraId'] + ret.offset_x = data['OffsetX'] + ret.offset_y = data['OffsetY'] + ret.offset_z = data['OffsetZ'] + ret.rot_h = data['RotH'] + ret.rot_v = data['RotV'] + ret.distance = data['Distance'] + ret.near = data['Near'] + ret.far = data['Far'] + ret.fov = data['Fov'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.battle_camera_id) \ + + encode_str(self.offset_x) \ + + encode_str(self.offset_y) \ + + encode_str(self.offset_z) \ + + encode_str(self.rot_h) \ + + encode_str(self.rot_v) \ + + encode_str(self.distance) \ + + encode_str(self.near) \ + + encode_str(self.far) \ + + encode_str(self.fov) + +class ChatMainStoryData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.chat_main_story_id = decode_int(data, off) + off += INT_OFF + + self.release_condition_type = decode_byte(data, off) + off += BYTE_OFF + + self.release_condition_value = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self.first_reward_vp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ChatMainStoryData": + ret = cls(b"\x00" * 99, 0) + ret.chat_main_story_id = data['ChatMainStoryId'] + ret.release_condition_type = data['ReleaseConditionType'] + ret.release_condition_value = data['ReleaseConditionValue'] + ret.display_name = data['DisplayName'] + ret.first_reward_vp = data['FirstRewardVp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.chat_main_story_id) \ + + encode_byte(self.release_condition_type) \ + + encode_int(self.release_condition_value) \ + + encode_str(self.display_name) \ + + encode_int(self.first_reward_vp) + +class ChatSideStoryData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.chat_side_story_id = decode_int(data, off) + off += INT_OFF + + self.unlock_side_quest_id = decode_int(data, off) + off += INT_OFF + + self.release_condition_chara_id = decode_short(data, off) + off += SHORT_OFF + + self.release_condition_type = decode_byte(data, off) + off += BYTE_OFF + + self.release_condition_value1 = decode_int(data, off) + off += INT_OFF + + self.release_condition_value2 = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self.first_reward_vp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ChatSideStoryData": + ret = cls(b"\x00" * 99, 0) + ret.chat_side_story_id = data['ChatSideStoryId'] + ret.unlock_side_quest_id = data['UnlockSideQuestId'] + ret.release_condition_chara_id = data['ReleaseConditionCharaId'] + ret.release_condition_type = data['ReleaseConditionType'] + ret.release_condition_value1 = data['ReleaseConditionValue1'] + ret.release_condition_value2 = data['ReleaseConditionValue2'] + ret.display_name = data['DisplayName'] + ret.first_reward_vp = data['FirstRewardVp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.chat_side_story_id) \ + + encode_int(self.unlock_side_quest_id) \ + + encode_short(self.release_condition_chara_id) \ + + encode_byte(self.release_condition_type) \ + + encode_int(self.release_condition_value1) \ + + encode_int(self.release_condition_value2) \ + + encode_str(self.display_name) \ + + encode_int(self.first_reward_vp) + +class ChatEventStoryData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.chat_event_story_id = decode_int(data, off) + off += INT_OFF + + self.event_id = decode_int(data, off) + off += INT_OFF + + self.release_condition_type = decode_byte(data, off) + off += BYTE_OFF + + self.release_condition_value = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self.first_reward_vp = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ChatEventStoryData": + ret = cls(b"\x00" * 99, 0) + ret.chat_event_story_id = data['ChatEventStoryId'] + ret.event_id = data['EventId'] + ret.release_condition_type = data['ReleaseConditionType'] + ret.release_condition_value = data['ReleaseConditionValue'] + ret.display_name = data['DisplayName'] + ret.first_reward_vp = data['FirstRewardVp'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.chat_event_story_id) \ + + encode_int(self.event_id) \ + + encode_byte(self.release_condition_type) \ + + encode_int(self.release_condition_value) \ + + encode_str(self.display_name) \ + + encode_int(self.first_reward_vp) + +class NavigatorCharaData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.navigator_chara_id = decode_int(data, off) + off += INT_OFF + + self.condition_start_episode_id = decode_int(data, off) + off += INT_OFF + + self.condition_end_episode_id = decode_int(data, off) + off += INT_OFF + + self.disp_chara = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "NavigatorCharaData": + ret = cls(b"\x00" * 99, 0) + ret.navigator_chara_id = data['NavigatorCharaId'] + ret.condition_start_episode_id = data['ConditionStartEpisodeId'] + ret.condition_end_episode_id = data['ConditionEndEpisodeId'] + ret.disp_chara = data['DispChara'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.navigator_chara_id) \ + + encode_int(self.condition_start_episode_id) \ + + encode_int(self.condition_end_episode_id) \ + + encode_int(self.disp_chara) + +class NavigatorCommentData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.navigator_comment_id = decode_int(data, off) + off += INT_OFF + + self.menu_id = decode_int(data, off) + off += INT_OFF + + self.draw_type = decode_int(data, off) + off += INT_OFF + + self.condition_past_menu = decode_int(data, off) + off += INT_OFF + + self.priority = decode_int(data, off) + off += INT_OFF + + self.condition_start_episode_id = decode_int(data, off) + off += INT_OFF + + self.condition_end_episode_id = decode_int(data, off) + off += INT_OFF + + self.only_once = decode_byte(data, off) + off += BYTE_OFF + + self.only_rico = decode_byte(data, off) + off += BYTE_OFF + + self.comment_ai, new_off = decode_str(data, off) + off += new_off + + self.comment_rico, new_off = decode_str(data, off) + off += new_off + + self.expression = decode_int(data, off) + off += INT_OFF + + self.voice_id, new_off = decode_str(data, off) + off += new_off + + self.show_start_time, new_off = decode_str(data, off) + off += new_off + + self.show_end_time, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "NavigatorCommentData": + ret = cls(b"\x00" * 99, 0) + ret.navigator_comment_id = data['NavigatorCommentId'] + ret.menu_id = data['MenuId'] + ret.draw_type = data['DrawType'] + ret.condition_past_menu = data['ConditionPastMenu'] + ret.priority = data['Priority'] + ret.condition_start_episode_id = data['ConditionStartEpisodeId'] + ret.condition_end_episode_id = data['ConditionEndEpisodeId'] + ret.only_once = data['OnlyOnce'] + ret.only_rico = data['OnlyRico'] + ret.comment_ai = data['CommentAi'] + ret.comment_rico = data['CommentRico'] + ret.expression = data['Expression'] + ret.voice_id = data['VoiceId'] + ret.show_start_time = data['ShowStartTime'] + ret.show_end_time = data['ShowEndTime'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.navigator_comment_id) \ + + encode_int(self.menu_id) \ + + encode_int(self.draw_type) \ + + encode_int(self.condition_past_menu) \ + + encode_int(self.priority) \ + + encode_int(self.condition_start_episode_id) \ + + encode_int(self.condition_end_episode_id) \ + + encode_byte(self.only_once) \ + + encode_byte(self.only_rico) \ + + encode_str(self.comment_ai) \ + + encode_str(self.comment_rico) \ + + encode_int(self.expression) \ + + encode_str(self.voice_id) \ + + encode_str(self.show_start_time) \ + + encode_str(self.show_end_time) + +class ExBonusTableData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ex_bonus_table_id = decode_int(data, off) + off += INT_OFF + + self.ex_bonus_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.ex_bonus_condition_id = decode_int(data, off) + off += INT_OFF + + self.condition_value1 = decode_int(data, off) + off += INT_OFF + + self.condition_value2 = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ExBonusTableData": + ret = cls(b"\x00" * 99, 0) + ret.ex_bonus_table_id = data['ExBonusTableId'] + ret.ex_bonus_table_sub_id = data['ExBonusTableSubId'] + ret.ex_bonus_condition_id = data['ExBonusConditionId'] + ret.condition_value1 = data['ConditionValue1'] + ret.condition_value2 = data['ConditionValue2'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.ex_bonus_table_id) \ + + encode_int(self.ex_bonus_table_sub_id) \ + + encode_int(self.ex_bonus_condition_id) \ + + encode_int(self.condition_value1) \ + + encode_int(self.condition_value2) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class ExBonusConditionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ex_bonus_condition_id = decode_int(data, off) + off += INT_OFF + + self.format, new_off = decode_str(data, off) + off += new_off + + self.hud_format, new_off = decode_str(data, off) + off += new_off + + self.format_param_size = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ExBonusConditionData": + ret = cls(b"\x00" * 99, 0) + ret.ex_bonus_condition_id = data['ExBonusConditionId'] + ret.format = data['Format'] + ret.hud_format = data['HudFormat'] + ret.format_param_size = data['FomatParamSize'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.ex_bonus_condition_id) \ + + encode_str(self.format) \ + + encode_str(self.hud_format) \ + + encode_int(self.format_param_size) + +class BeginnerMissionProgressesUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.beginner_mission_condition_id = decode_int(data, off) + off += INT_OFF + + self.achievement_num = decode_short(data, off) + off += SHORT_OFF + + self.complete_flag = decode_byte(data, off) + off += BYTE_OFF + + self.complete_date, new_off = decode_date_str(data, off) + off += new_off + + self.reward_received_flag = decode_byte(data, off) + off += BYTE_OFF + + self.reward_received_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BeginnerMissionProgressesUserData": + ret = cls(b"\x00" * 99, 0) + ret.beginner_mission_condition_id = data['BeginnerMissionConditionId'] + ret.achievement_num = data['AchievementNum'] + ret.complete_flag = data['CompleteFlag'] + ret.complete_date = data['CompleteDate'] + ret.reward_received_flag = data['RewardReceivedFlag'] + ret.reward_received_date = data['RewardReceivedDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.beginner_mission_condition_id) \ + + encode_short(self.achievement_num) \ + + encode_byte(self.complete_flag) \ + + encode_date_str(self.complete_date) \ + + encode_byte(self.reward_received_flag) \ + + encode_date_str(self.reward_received_date) + +class BeginnerMissionSeatProgressesUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.beginner_mission_seat_condition_id = decode_int(data, off) + off += INT_OFF + + self.achievement_num = decode_short(data, off) + off += SHORT_OFF + + self.complete_flag = decode_byte(data, off) + off += BYTE_OFF + + self.complete_date, new_off = decode_date_str(data, off) + off += new_off + + self.reward_received_flag = decode_byte(data, off) + off += BYTE_OFF + + self.reward_received_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BeginnerMissionSeatProgressesUserData": + ret = cls(b"\x00" * 99, 0) + ret.beginner_mission_seat_condition_id = data['BeginnerMissionSeatConditionId'] + ret.achievement_num = data['AchievementNum'] + ret.complete_flag = data['CompleteFlag'] + ret.complete_date = data['CompleteDate'] + ret.reward_received_flag = data['RewardReceivedFlag'] + ret.reward_received_date = data['RewardReceivedDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.beginner_mission_seat_condition_id) \ + + encode_short(self.achievement_num) \ + + encode_byte(self.complete_flag) \ + + encode_date_str(self.complete_date) \ + + encode_byte(self.reward_received_flag) \ + + encode_date_str(self.reward_received_date) + +class LinkedSiteRegCampaignUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.reward_received_flag = decode_byte(data, off) + off += BYTE_OFF + + self.reward_received_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "LinkedSiteRegCampaignUserData": + ret = cls(b"\x00" * 99, 0) + ret.reward_received_flag = data['RewardReceivedFlag'] + ret.reward_received_date = data['RewardReceivedDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.reward_received_flag) \ + + encode_date_str(self.reward_received_date) + +class HeroLogUnitUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.hero_log_id = decode_int(data, off) + off += INT_OFF + + self.leader_appointment_normal_card_num = decode_int(data, off) + off += INT_OFF + + self.leader_appointment_holographic_card_num = decode_int(data, off) + off += INT_OFF + + self.skill_slot_max_release_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "HeroLogUnitUserData": + ret = cls(b"\x00" * 13, 0) + ret.hero_log_id = data['HeroLogId'] + ret.leader_appointment_normal_card_num = data['LeaderAppointmentNormalCardNum'] + ret.leader_appointment_holographic_card_num = data['LeaderAppointmentHolographicCardNum'] + ret.skill_slot_max_release_flag = data['SkillSlotMaxReleaseFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.hero_log_id) \ + + encode_int(self.leader_appointment_normal_card_num) \ + + encode_int(self.leader_appointment_holographic_card_num) \ + + encode_byte(self.skill_slot_max_release_flag) + +class CharaUnitUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.chara_id = decode_short(data, off) + off += SHORT_OFF + + self.reliability_1 = decode_int(data, off) + off += INT_OFF + + self.reliability_2 = decode_int(data, off) + off += INT_OFF + + self.motivation_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "CharaUnitUserData": + ret = cls(b"\x00" * 99, 0) + ret.chara_id = data['CharaId'] + ret.reliability_1 = data['Reliability1'] + ret.reliability_2 = data['Reliability2'] + ret.motivation_flag = data['MotivationFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.chara_id) \ + + encode_int(self.reliability_1) \ + + encode_int(self.reliability_2) \ + + encode_byte(self.motivation_flag) + +class AdventureExecUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.status = decode_byte(data, off) + off += BYTE_OFF + + self.adventure_id = decode_int(data, off) + off += INT_OFF + + self.adventure_difficulty_id = decode_int(data, off) + off += INT_OFF + + self.last_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.remaining_sec_to_complete = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "AdventureExecUserData": + ret = cls(b"\x00" * 99, 0) + ret.status = data['Status'] + ret.adventure_id = data['AdventureId'] + ret.adventure_difficulty_id = data['AdventureDifficultyId'] + ret.last_start_date = data['LastStartDate'] + ret.remaining_sec_to_complete = data['RemainingSecToComplete'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.status) \ + + encode_int(self.adventure_id) \ + + encode_int(self.adventure_difficulty_id) \ + + encode_date_str(self.last_start_date) \ + + encode_int(self.remaining_sec_to_complete) + +class QuestRareDropData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_rare_drop_id = decode_int(data, off) + off += INT_OFF + + self.quest_scene_id = decode_int(data, off) + off += INT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_int(data, off) + off += INT_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_int(data, off) + off += INT_OFF + + self.strength_min = decode_int(data, off) + off += INT_OFF + + self.strength_max = decode_int(data, off) + off += INT_OFF + + self.property_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.drop_rate, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestRareDropData": + ret = cls(b"\x00" * 99, 0) + ret.quest_rare_drop_id = data['QuestRareDropId'] + ret.quest_scene_id = data['QuestSceneId'] + ret.unit_id = data['UnitId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength_min = data['StrengthMin'] + ret.strength_max = data['StrengthMax'] + ret.property_table_sub_id = data['PropertyTableSubId'] + ret.drop_rate = data['DropRate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.quest_rare_drop_id) \ + + encode_int(self.quest_scene_id) \ + + encode_int(self.unit_id) \ + + encode_int(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_int(self.common_reward_num) \ + + encode_int(self.strength_min) \ + + encode_int(self.strength_max) \ + + encode_int(self.property_table_sub_id) \ + + encode_str(self.drop_rate) + +class QuestSpecialRareDropSettingData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_special_rare_drop_setting_id = decode_int(data, off) + off += INT_OFF + + self.tower_type = decode_byte(data, off) + off += BYTE_OFF + + self.start_trial_tower_id = decode_short(data, off) + off += SHORT_OFF + + self.end_trial_tower_id = decode_short(data, off) + off += SHORT_OFF + + self.rare_drop_upward_rate, new_off = decode_str(data, off) + off += new_off + + self.quest_special_rare_drop_sub_id = decode_int(data, off) + off += INT_OFF + + self.generic_campaign_period_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestSpecialRareDropSettingData": + ret = cls(b"\x00" * 99, 0) + ret.quest_special_rare_drop_setting_id = data['QuestSpecialRareDropSettingId'] + ret.tower_type = data['TowerType'] + ret.start_trial_tower_id = data['StartTrialTowerId'] + ret.end_trial_tower_id = data['EndTrialTowerId'] + ret.rare_drop_upward_rate = data['RareDropUpwardRate'] + ret.quest_special_rare_drop_sub_id = data['QuestSpecialRareDropSubId'] + ret.generic_campaign_period_id = data['GenericCampaignPeriodId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.quest_special_rare_drop_setting_id) \ + + encode_byte(self.tower_type) \ + + encode_short(self.start_trial_tower_id) \ + + encode_short(self.end_trial_tower_id) \ + + encode_str(self.rare_drop_upward_rate) \ + + encode_int(self.quest_special_rare_drop_sub_id) \ + + encode_int(self.generic_campaign_period_id) + +class QuestSpecialRareDropData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_special_rare_drop_id = decode_int(data, off) + off += INT_OFF + + self.quest_special_rare_drop_sub_id = decode_int(data, off) + off += INT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_int(data, off) + off += INT_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_int(data, off) + off += INT_OFF + + self.strength_min = decode_int(data, off) + off += INT_OFF + + self.strength_max = decode_int(data, off) + off += INT_OFF + + self.property_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.drop_rate, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestSpecialRareDropData": + ret = cls(b"\x00" * 99, 0) + ret.quest_special_rare_drop_id = data['QuestSpecialRareDropId'] + ret.quest_special_rare_drop_sub_id = data['QuestSpecialRareDropSubId'] + ret.unit_id = data['UnitId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength_min = data['StrengthMin'] + ret.strength_max = data['StrengthMax'] + ret.property_table_sub_id = data['PropertyTableSubId'] + ret.drop_rate = data['DropRate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.quest_special_rare_drop_id) \ + + encode_int(self.quest_special_rare_drop_sub_id) \ + + encode_int(self.unit_id) \ + + encode_int(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_int(self.common_reward_num) \ + + encode_int(self.strength_min) \ + + encode_int(self.strength_max) \ + + encode_int(self.property_table_sub_id) \ + + encode_str(self.drop_rate) + +class QuestTutorialData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_tutorial_id = decode_short(data, off) + off += SHORT_OFF + + self.trigger = decode_int(data, off) + off += INT_OFF + + self.trigger_delay, new_off = decode_str(data, off) + off += new_off + + self.trigger_during, new_off = decode_str(data, off) + off += new_off + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.unit_range, new_off = decode_str(data, off) + off += new_off + + self.comment_text, new_off = decode_str(data, off) + off += new_off + + self.voice_id, new_off = decode_str(data, off) + off += new_off + + self.comment_pos, new_off = decode_str(data, off) + off += new_off + + self.yui_face, new_off = decode_str(data, off) + off += new_off + + self.arrow_direct, new_off = decode_str(data, off) + off += new_off + + self.arrow_position, new_off = decode_str(data, off) + off += new_off + + self.image_plus, new_off = decode_str(data, off) + off += new_off + + self.image_plus_pos, new_off = decode_str(data, off) + off += new_off + + self.stick_info, new_off = decode_str(data, off) + off += new_off + + self.mark_effect, new_off = decode_str(data, off) + off += new_off + + self.mark_pos, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestTutorialData": + ret = cls(b"\x00" * 99, 0) + ret.quest_tutorial_id = data['Id'] + ret.trigger = data['Trigger'] + ret.trigger_delay = data['TriggerDelay'] + ret.trigger_during = data['TriggerDuring'] + ret.unit_id = data['UnitId'] + ret.unit_range = data['UnitRange'] + ret.comment_text = data['CommentText'] + ret.voice_id = data['VoiceID'] + ret.comment_pos = data['CommentPos'] + ret.yui_face = data['YuiFace'] + ret.arrow_direct = data['ArrowDirect'] + ret.arrow_position = data['ArrowPosition'] + ret.image_plus = data['ImagePlus'] + ret.image_plus_pos = data['ImagePlusPos'] + ret.stick_info = data['StickInfo'] + ret.mark_effect = data['MarkEffect'] + ret.mark_pos = data['MarkPos'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.quest_tutorial_id) \ + + encode_int(self.trigger) \ + + encode_str(self.trigger_delay) \ + + encode_str(self.trigger_during) \ + + encode_int(self.unit_id) \ + + encode_str(self.unit_range) \ + + encode_str(self.comment_text) \ + + encode_str(self.voice_id) \ + + encode_str(self.comment_pos) \ + + encode_str(self.yui_face) \ + + encode_str(self.arrow_direct) \ + + encode_str(self.arrow_position) \ + + encode_str(self.image_plus) \ + + encode_str(self.image_plus_pos) \ + + encode_str(self.stick_info) \ + + encode_str(self.mark_effect) \ + + encode_str(self.mark_pos) + +class QuestPlayerTraceTableData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.player_trace_table_id = decode_short(data, off) + off += SHORT_OFF + + self.player_trace_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.rate = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestPlayerTraceTableData": + ret = cls(b"\x00" * 99, 0) + ret.player_trace_table_id = data['PlayerTraceTableId'] + ret.player_trace_table_sub_id = data['PlayerTraceTableSubId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.rate = data['Rate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.player_trace_table_id) \ + + encode_int(self.player_trace_table_sub_id) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.rate) + +class QuestStillData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.still_id = decode_int(data, off) + off += INT_OFF + + self.file_name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestStillData": + ret = cls(b"\x00" * 99, 0) + ret.still_id = data['StillId'] + ret.file_name = data['FileName'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.still_id) \ + + encode_str(self.file_name) + +class GashaData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.description, new_off = decode_str(data, off) + off += new_off + + self.gasha_type = decode_byte(data, off) + off += BYTE_OFF + + self.open_type = decode_byte(data, off) + off += BYTE_OFF + + self.free_target_flag = decode_byte(data, off) + off += BYTE_OFF + + self.reset_hour_list, new_off = decode_str(data, off) + off += new_off + + self.limit_num = decode_short(data, off) + off += SHORT_OFF + + self.open_days = decode_short(data, off) + off += SHORT_OFF + + self.sort_num = decode_int(data, off) + off += INT_OFF + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_id = data['GashaId'] + ret.name = data['Name'] + ret.description = data['Description'] + ret.gasha_type = data['GashaType'] + ret.open_type = data['OpenType'] + ret.free_target_flag = data['FreeTargetFlag'] + ret.reset_hour_list = data['ResetHourList'] + ret.limit_num = data['LimitNum'] + ret.open_days = data['OpenDays'] + ret.sort_num = data['SortNum'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_id) \ + + encode_str(self.name) \ + + encode_str(self.description) \ + + encode_byte(self.gasha_type) \ + + encode_byte(self.open_type) \ + + encode_byte(self.free_target_flag) \ + + encode_str(self.reset_hour_list) \ + + encode_short(self.limit_num) \ + + encode_short(self.open_days) \ + + encode_int(self.sort_num) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class GashaHeaderData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_header_id = decode_int(data, off) + off += INT_OFF + + self.gasha_id = decode_int(data, off) + off += INT_OFF + + self.step_no = decode_byte(data, off) + off += BYTE_OFF + + self.step_progress_flag = decode_byte(data, off) + off += BYTE_OFF + + self.platform = decode_byte(data, off) + off += BYTE_OFF + + self.use_type_1 = decode_byte(data, off) + off += BYTE_OFF + + self.use_type_2 = decode_byte(data, off) + off += BYTE_OFF + + self.provision_num = decode_byte(data, off) + off += BYTE_OFF + + self.premium_lottery_num = decode_byte(data, off) + off += BYTE_OFF + + self.premium_lottery_executable_num = decode_byte(data, off) + off += BYTE_OFF + + self.use_type_1_first_use_num = decode_int(data, off) + off += INT_OFF + + self.use_type_1_base_use_num = decode_int(data, off) + off += INT_OFF + + self.use_type_1_extra_use_num = decode_int(data, off) + off += INT_OFF + + self.use_type_2_first_use_num = decode_int(data, off) + off += INT_OFF + + self.use_type_2_base_use_num = decode_int(data, off) + off += INT_OFF + + self.use_type_2_extra_use_num = decode_int(data, off) + off += INT_OFF + + self.yui_chance_log_open_rate, new_off = decode_str(data, off) + off += new_off + + self.yui_chance_rarity_up_rate, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaHeaderData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_header_id = data['GashaHeaderId'] + ret.gasha_id = data['GashaId'] + ret.step_no = data['StepNo'] + ret.step_progress_flag = data['StepProgressFlag'] + ret.platform = data['Platform'] + ret.use_type_1 = data['UseType1'] + ret.use_type_2 = data['UseType2'] + ret.provision_num = data['ProvisionNum'] + ret.premium_lottery_num = data['PremiumLotteryNum'] + ret.premium_lottery_executable_num = data['PremiumLotteryExecutableNum'] + ret.use_type_1_first_use_num = data['UseType1FirstUseNum'] + ret.use_type_1_base_use_num = data['UseType1BaseUseNum'] + ret.use_type_1_extra_use_num = data['UseType1ExtraUseNum'] + ret.use_type_2_first_use_num = data['UseType2FirstUseNum'] + ret.use_type_2_base_use_num = data['UseType2BaseUseNum'] + ret.use_type_2_extra_use_num = data['UseType2ExtraUseNum'] + ret.yui_chance_log_open_rate = data['YuiChanceLogOpenRate'] + ret.yui_chance_rarity_up_rate = data['YuiChanceRarityUpRate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_header_id) \ + + encode_int(self.gasha_id) \ + + encode_byte(self.step_no) \ + + encode_byte(self.step_progress_flag) \ + + encode_byte(self.platform) \ + + encode_byte(self.use_type_1) \ + + encode_byte(self.use_type_2) \ + + encode_byte(self.provision_num) \ + + encode_byte(self.premium_lottery_num) \ + + encode_byte(self.premium_lottery_executable_num) \ + + encode_int(self.use_type_1_first_use_num) \ + + encode_int(self.use_type_1_base_use_num) \ + + encode_int(self.use_type_1_extra_use_num) \ + + encode_int(self.use_type_2_first_use_num) \ + + encode_int(self.use_type_2_base_use_num) \ + + encode_int(self.use_type_2_extra_use_num) \ + + encode_str(self.yui_chance_log_open_rate) \ + + encode_str(self.yui_chance_rarity_up_rate) + +class GashaLotteryRarityData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_lottery_rarity_id = decode_int(data, off) + off += INT_OFF + + self.gasha_id = decode_int(data, off) + off += INT_OFF + + self.gasha_header_id = decode_int(data, off) + off += INT_OFF + + self.lottery_rarity_type = decode_byte(data, off) + off += BYTE_OFF + + self.lottery_rarity_rate, new_off = decode_str(data, off) + off += new_off + + self.premium_rate, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaLotteryRarityData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_lottery_rarity_id = data['GashaLotteryRarityId'] + ret.gasha_id = data['GashaId'] + ret.gasha_header_id = data['GashaHeaderId'] + ret.lottery_rarity_type = data['LotteryRarityType'] + ret.lottery_rarity_rate = data['LotteryRarityRate'] + ret.premium_rate = data['PremiumRate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_lottery_rarity_id) \ + + encode_int(self.gasha_id) \ + + encode_int(self.gasha_header_id) \ + + encode_byte(self.lottery_rarity_type) \ + + encode_str(self.lottery_rarity_rate) \ + + encode_str(self.premium_rate) + +class GashaPrizeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_prize_id = decode_int(data, off) + off += INT_OFF + + self.gasha_id = decode_int(data, off) + off += INT_OFF + + self.lottery_rarity_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_type = decode_int(data, off) + off += INT_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_int(data, off) + off += INT_OFF + + self.unanalyzed_log_grade_id = decode_int(data, off) + off += INT_OFF + + self.correction_rate = decode_short(data, off) + off += SHORT_OFF + + self.pickup_flag = decode_byte(data, off) + off += BYTE_OFF + + self.property_table_sub_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaPrizeData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_prize_id = data['GashaPrizeId'] + ret.gasha_id = data['GashaId'] + ret.lottery_rarity_type = data['LotteryRarityType'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.unanalyzed_log_grade_id = data['UnanalyzedLogGradeId'] + ret.correction_rate = data['CorrectionRate'] + ret.pickup_flag = data['PickupFlag'] + ret.property_table_sub_id = data['PropertyTableSubId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_prize_id) \ + + encode_int(self.gasha_id) \ + + encode_byte(self.lottery_rarity_type) \ + + encode_int(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_int(self.common_reward_num) \ + + encode_int(self.unanalyzed_log_grade_id) \ + + encode_short(self.correction_rate) \ + + encode_byte(self.pickup_flag) \ + + encode_int(self.property_table_sub_id) + +class ComebackEventData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.comeback_event_id = decode_int(data, off) + off += INT_OFF + + self.comeback_event_sub_id = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self.reward_set_sub_id = decode_int(data, off) + off += INT_OFF + + self.require_days = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ComebackEventData": + ret = cls(b"\x00" * 99, 0) + ret.comeback_event_id = data['ComebackEventId'] + ret.comeback_event_sub_id = data['ComebackEventSubId'] + ret.display_name = data['DisplayName'] + ret.reward_set_sub_id = data['RewardSetSubId'] + ret.require_days = data['RequireDays'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.comeback_event_id) \ + + encode_int(self.comeback_event_sub_id) \ + + encode_str(self.display_name) \ + + encode_int(self.reward_set_sub_id) \ + + encode_short(self.require_days) + +class AdBannerData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ad_banner_id = decode_int(data, off) + off += INT_OFF + + self.category = decode_byte(data, off) + off += BYTE_OFF + + self.sort_num = decode_int(data, off) + off += INT_OFF + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self.active_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "AdBannerData": + ret = cls(b"\x00" * 99, 0) + ret.ad_banner_id = data['AdBannerId'] + ret.category = data['Category'] + ret.sort_num = data['SortNum'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + ret.active_flag = data['ActiveFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.ad_banner_id) \ + + encode_byte(self.category) \ + + encode_int(self.sort_num) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) \ + + encode_byte(self.active_flag) + +class EventsData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.event_id = decode_int(data, off) + off += INT_OFF + + self.event_type = decode_byte(data, off) + off += BYTE_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.param_1, new_off = decode_str(data, off) + off += new_off + + self.param_2, new_off = decode_str(data, off) + off += new_off + + self.param_3, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.posting_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.posting_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.chat_open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.chat_open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.news_id = decode_int(data, off) + off += INT_OFF + + self.help_id = decode_int(data, off) + off += INT_OFF + + self.ad_banner_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EventsData": + ret = cls(b"\x00" * 99, 0) + ret.event_id = data['EventId'] + ret.event_type = data['EventType'] + ret.title = data['Title'] + ret.param_1 = data['Param1'] + ret.param_2 = data['Param2'] + ret.param_3 = data['Param3'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + ret.posting_start_date = data['PostingStartDate'] + ret.posting_end_date = data['PostingEndDate'] + ret.chat_open_start_date = data['ChatOpenStartDate'] + ret.chat_open_end_date = data['ChatOpenEndDate'] + ret.news_id = data['NewId'] + ret.help_id = data['HelpId'] + ret.ad_banner_id = data['AdBannerId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.event_id) \ + + encode_byte(self.event_type) \ + + encode_str(self.title) \ + + encode_str(self.param_1) \ + + encode_str(self.param_2) \ + + encode_str(self.param_3) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) \ + + encode_date_str(self.posting_start_date) \ + + encode_date_str(self.posting_end_date) \ + + encode_date_str(self.chat_open_start_date) \ + + encode_date_str(self.chat_open_end_date) \ + + encode_int(self.news_id) \ + + encode_int(self.help_id) \ + + encode_int(self.ad_banner_id) + +class TreasureHuntsData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.treasure_hunt_id = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TreasureHuntsData": + ret = cls(b"\x00" * 99, 0) + ret.treasure_hunt_id = data['TreasureHuntId'] + ret.title = data['Title'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.treasure_hunt_id) \ + + encode_str(self.title) + +class TreasureHuntWholeTasksData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.treasure_hunt_whole_task_id = decode_int(data, off) + off += INT_OFF + + self.treasure_hunt_id = decode_int(data, off) + off += INT_OFF + + self.task_type = decode_byte(data, off) + off += BYTE_OFF + + self.achievement_possible_num = decode_short(data, off) + off += SHORT_OFF + + self.get_event_point = decode_int(data, off) + off += INT_OFF + + self.condition_type = decode_byte(data, off) + off += BYTE_OFF + + self.condition_value_accumulation_flag = decode_byte(data, off) + off += BYTE_OFF + + self.condition_value_1, new_off = decode_str(data, off) + off += new_off + + self.condition_value_2, new_off = decode_str(data, off) + off += new_off + + self.condition_value_3, new_off = decode_str(data, off) + off += new_off + + self.condition_value_4, new_off = decode_str(data, off) + off += new_off + + self.condition_value_5, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TreasureHuntWholeTasksData": + ret = cls(b"\x00" * 99, 0) + ret.treasure_hunt_whole_task_id = data['TreasureHuntWholeTaskId'] + ret.treasure_hunt_id = data['TreasureHuntId'] + ret.task_type = data['TaskType'] + ret.achievement_possible_num = data['AchievementPossibleNum'] + ret.get_event_point = data['GetEventPoint'] + ret.condition_type = data['ConditionType'] + ret.condition_value_accumulation_flag = data['ConditionValueAccumulationFlag'] + ret.condition_value_1 = data['ConditionValue1'] + ret.condition_value_2 = data['ConditionValue2'] + ret.condition_value_3 = data['ConditionValue3'] + ret.condition_value_4 = data['ConditionValue4'] + ret.condition_value_5 = data['ConditionValue5'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.treasure_hunt_whole_task_id) \ + + encode_int(self.treasure_hunt_id) \ + + encode_byte(self.task_type) \ + + encode_short(self.achievement_possible_num) \ + + encode_int(self.get_event_point) \ + + encode_byte(self.condition_type) \ + + encode_byte(self.condition_value_accumulation_flag) \ + + encode_str(self.condition_value_1) \ + + encode_str(self.condition_value_2) \ + + encode_str(self.condition_value_3) \ + + encode_str(self.condition_value_4) \ + + encode_str(self.condition_value_5) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) + +class TreasureHuntIndividualTasksData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.treasure_hunt_individual_task_id = decode_int(data, off) + off += INT_OFF + + self.treasure_hunt_id = decode_int(data, off) + off += INT_OFF + + self.task_type = decode_byte(data, off) + off += BYTE_OFF + + self.quest_scene_id = decode_short(data, off) + off += SHORT_OFF + + self.achievement_possible_num = decode_short(data, off) + off += SHORT_OFF + + self.get_event_point = decode_int(data, off) + off += INT_OFF + + self.condition_type = decode_byte(data, off) + off += BYTE_OFF + + self.condition_value_accumulation_flag = decode_byte(data, off) + off += BYTE_OFF + + self.condition_value_1, new_off = decode_str(data, off) + off += new_off + + self.condition_value_2, new_off = decode_str(data, off) + off += new_off + + self.condition_value_3, new_off = decode_str(data, off) + off += new_off + + self.condition_value_4, new_off = decode_str(data, off) + off += new_off + + self.condition_value_5, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TreasureHuntIndividualTasksData": + ret = cls(b"\x00" * 99, 0) + ret.treasure_hunt_individual_task_id = data['TreasureHuntIndividualTaskId'] + ret.treasure_hunt_id = data['TreasureHuntId'] + ret.task_type = data['TaskType'] + ret.quest_scene_id = data['QuestSceneId'] + ret.achievement_possible_num = data['AchievementPossibleNum'] + ret.get_event_point = data['GetEventPoint'] + ret.condition_type = data['ConditionType'] + ret.condition_value_accumulation_flag = data['ConditionValueAccumulationFlag'] + ret.condition_value_1 = data['ConditionValue1'] + ret.condition_value_2 = data['ConditionValue2'] + ret.condition_value_3 = data['ConditionValue3'] + ret.condition_value_4 = data['ConditionValue4'] + ret.condition_value_5 = data['ConditionValue5'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.treasure_hunt_individual_task_id) \ + + encode_int(self.treasure_hunt_id) \ + + encode_byte(self.task_type) \ + + encode_short(self.quest_scene_id) \ + + encode_short(self.achievement_possible_num) \ + + encode_int(self.get_event_point) \ + + encode_byte(self.condition_type) \ + + encode_byte(self.condition_value_accumulation_flag) \ + + encode_str(self.condition_value_1) \ + + encode_str(self.condition_value_2) \ + + encode_str(self.condition_value_3) \ + + encode_str(self.condition_value_4) \ + + encode_str(self.condition_value_5) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) + +class TreasureHuntSpecialEffectsData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.treasure_hunt_special_effect_id = decode_int(data, off) + off += INT_OFF + + self.treasure_hunt_id = decode_int(data, off) + off += INT_OFF + + self.special_effect_type = decode_byte(data, off) + off += BYTE_OFF + + self.special_effect_coefficient, new_off = decode_str(data, off) + off += new_off + + self.target_common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.target_common_reward_id = decode_int(data, off) + off += INT_OFF + + self.obtaining_location_type = decode_byte(data, off) + off += BYTE_OFF + + self.obtaining_location_param, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TreasureHuntSpecialEffectsData": + ret = cls(b"\x00" * 99, 0) + ret.treasure_hunt_special_effect_id = data['TreasureHuntSpecialEffectId'] + ret.treasure_hunt_id = data['TreasureHuntId'] + ret.special_effect_type = data['SpecialEffectType'] + ret.special_effect_coefficient = data['SpecialEffectCoefficient'] + ret.target_common_reward_type = data['TargetCommonRewardType'] + ret.target_common_reward_id = data['TargetCommonRewardId'] + ret.obtaining_location_type = data['ObtainingLocationType'] + ret.obtaining_location_param = data['ObtainingLocationParam'] + ret.open_start_date = data['OpenStartDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.treasure_hunt_special_effect_id) \ + + encode_int(self.treasure_hunt_id) \ + + encode_byte(self.special_effect_type) \ + + encode_str(self.special_effect_coefficient) \ + + encode_byte(self.target_common_reward_type) \ + + encode_int(self.target_common_reward_id) \ + + encode_byte(self.obtaining_location_type) \ + + encode_str(self.obtaining_location_param) \ + + encode_date_str(self.open_start_date) + +class TreasureHuntEventPointRewardCommonRewardsData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.treasure_hunt_event_point_reward_common_reward_id = decode_int(data, off) + off += INT_OFF + + self.treasure_hunt_id = decode_int(data, off) + off += INT_OFF + + self.need_event_point = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TreasureHuntEventPointRewardCommonRewardsData": + ret = cls(b"\x00" * 99, 0) + ret.treasure_hunt_event_point_reward_common_reward_id = data['TreasureHuntEventPointRewardCommonRewardId'] + ret.treasure_hunt_id = data['TreasureHuntId'] + ret.need_event_point = data['NeedEventPoint'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.treasure_hunt_event_point_reward_common_reward_id) \ + + encode_int(self.treasure_hunt_id) \ + + encode_int(self.need_event_point) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class TreasureHuntEventPointRewardTitlesData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.treasure_hunt_event_point_reward_title_id = decode_int(data, off) + off += INT_OFF + + self.treasure_hunt_id = decode_int(data, off) + off += INT_OFF + + self.need_event_point = decode_int(data, off) + off += INT_OFF + + self.title_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TreasureHuntEventPointRewardTitlesData": + ret = cls(b"\x00" * 99, 0) + ret.treasure_hunt_event_point_reward_title_id = data['TreasureHuntEventPointRewardTitleId'] + ret.treasure_hunt_id = data['TreasureHuntId'] + ret.need_event_point = data['NeedEventPoint'] + ret.title_id = data['TitleId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.treasure_hunt_event_point_reward_title_id) \ + + encode_int(self.treasure_hunt_id) \ + + encode_int(self.need_event_point) \ + + encode_int(self.title_id) + +class TreasureHuntTaskTextData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.treasure_hunt_task_text_id = decode_int(data, off) + off += INT_OFF + + self.task_type = decode_byte(data, off) + off += BYTE_OFF + + self.condition_type = decode_byte(data, off) + off += BYTE_OFF + + self.format, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "TreasureHuntTaskTextData": + ret = cls(b"\x00" * 99, 0) + ret.treasure_hunt_task_text_id = data['TreasureHuntTaskTextId'] + ret.task_type = data['TaskType'] + ret.condition_type = data['ConditionType'] + ret.format = data['Format'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.treasure_hunt_task_text_id) \ + + encode_byte(self.task_type) \ + + encode_byte(self.condition_type) \ + + encode_str(self.format) + +class BnidSerialCodeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.bnid_item_id, new_off = decode_str(data, off) + off += new_off + + self.serial_code_type = decode_byte(data, off) + off += BYTE_OFF + + self.category = decode_byte(data, off) + off += BYTE_OFF + + self.description, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BnidSerialCodeData": + ret = cls(b"\x00" * 99, 0) + ret.bnid_item_id = data['BnidItemId'] + ret.serial_code_type = data['SerialCodeType'] + ret.category = data['Category'] + ret.description = data['Description'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.bnid_item_id) \ + + encode_byte(self.serial_code_type) \ + + encode_byte(self.category) \ + + encode_str(self.description) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) + +class BnidSerialCodeRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.bnid_item_id, new_off = decode_str(data, off) + off += new_off + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BnidSerialCodeRewardData": + ret = cls(b"\x00" * 99, 0) + ret.bnid_item_id = data['BnidItemId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.bnid_item_id) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class SupportLogData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.support_log_id = decode_int(data, off) + off += INT_OFF + + self.chara_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.rarity = decode_byte(data, off) + off += BYTE_OFF + + self.support_log_type_id = decode_short(data, off) + off += SHORT_OFF + + self.sale_price = decode_int(data, off) + off += INT_OFF + + self.composition_exp = decode_int(data, off) + off += INT_OFF + + self.awakening_exp = decode_int(data, off) + off += INT_OFF + + self.use_rank = decode_short(data, off) + off += SHORT_OFF + + self.group_no = decode_short(data, off) + off += SHORT_OFF + + self.adaptable_appear_append = decode_short(data, off) + off += SHORT_OFF + + self.punisher_appear_append = decode_short(data, off) + off += SHORT_OFF + + self.state, new_off = decode_str(data, off) + off += new_off + + self.passive_state, new_off = decode_str(data, off) + off += new_off + + self.power_default_value = decode_int(data, off) + off += INT_OFF + + self.clt_default_value = decode_int(data, off) + off += INT_OFF + + self.awakening_1_power = decode_int(data, off) + off += INT_OFF + + self.awakening_1_clt = decode_int(data, off) + off += INT_OFF + + self.awakening_2_power = decode_int(data, off) + off += INT_OFF + + self.awakening_2_clt = decode_int(data, off) + off += INT_OFF + + self.awakening_3_power = decode_int(data, off) + off += INT_OFF + + self.awakening_3_clt = decode_int(data, off) + off += INT_OFF + + self.awakening_4_power = decode_int(data, off) + off += INT_OFF + + self.awakening_4_clt = decode_int(data, off) + off += INT_OFF + + self.awakening_5_power = decode_int(data, off) + off += INT_OFF + + self.awakening_5_clt = decode_int(data, off) + off += INT_OFF + + self.normal_leader = decode_int(data, off) + off += INT_OFF + + self.holo_leader = decode_int(data, off) + off += INT_OFF + + self.ui_display_power_title, new_off = decode_str(data, off) + off += new_off + + self.ui_display_power_content, new_off = decode_str(data, off) + off += new_off + + self.prefab, new_off = decode_str(data, off) + off += new_off + + self.cutin_mode, new_off = decode_str(data, off) + off += new_off + + self.skill_voice_id, new_off = decode_str(data, off) + off += new_off + + self.skill_name, new_off = decode_str(data, off) + off += new_off + + self.name_in_skill_cutin, new_off = decode_str(data, off) + off += new_off + + self.skill_text, new_off = decode_str(data, off) + off += new_off + + self.skill_text_in_skill_cutin, new_off = decode_str(data, off) + off += new_off + + self.chara_info, new_off = decode_str(data, off) + off += new_off + + self.cutin_image, new_off = decode_str(data, off) + off += new_off + + self.cutin_image_awake, new_off = decode_str(data, off) + off += new_off + + self.status_chara_icon, new_off = decode_str(data, off) + off += new_off + + self.status_chara_icon_awake, new_off = decode_str(data, off) + off += new_off + + self.collection_display_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.collection_empty_frame_display_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SupportLogData": + ret = cls(b"\x00" * 99, 0) + ret.support_log_id = data['SupportLogId'] + ret.chara_id = data['CharaId'] + ret.name = data['Name'] + ret.rarity = data['Rarity'] + ret.support_log_type_id = data['SupportLogTypeId'] + ret.sale_price = data['SalePrice'] + ret.composition_exp = data['CompositionExp'] + ret.awakening_exp = data['AwakeningExp'] + ret.use_rank = data['UseRank'] + ret.group_no = data['GroupNo'] + ret.adaptable_appear_append = data['AdaptableAppearAppend'] + ret.punisher_appear_append = data['PunisherAppearAppend'] + ret.state = data['State'] + ret.passive_state = data['PassiveState'] + ret.power_default_value = data['PowerDefaultValue'] + ret.clt_default_value = data['CltDefaultValue'] + ret.awakening_1_power = data['Awakening1Power'] + ret.awakening_1_clt = data['Awakening1Clt'] + ret.awakening_2_power = data['Awakening2Power'] + ret.awakening_2_clt = data['Awakening2Clt'] + ret.awakening_3_power = data['Awakening3Power'] + ret.awakening_3_clt = data['Awakening3Clt'] + ret.awakening_4_power = data['Awakening4Power'] + ret.awakening_4_clt = data['Awakening4Clt'] + ret.awakening_5_power = data['Awakening5Power'] + ret.awakening_5_clt = data['Awakening5Clt'] + ret.normal_leader = data['NormalLeader'] + ret.holo_leader = data['HoloLeader'] + ret.ui_display_power_title = data['UiDisplayPowerTitle'] + ret.ui_display_power_content = data['UiDisplayPowerContent'] + ret.prefab = data['Prefab'] + ret.cutin_mode = data['CutinMode'] + ret.skill_voice_id = data['SkillVoiceId'] + ret.skill_name = data['SkillName'] + ret.name_in_skill_cutin = data['NameInSkillCutin'] + ret.skill_text = data['SkillText'] + ret.skill_text_in_skill_cutin = data['SkillTextInSkillCutin'] + ret.chara_info = data['CharaInfo'] + ret.cutin_image = data['CutinImage'] + ret.cutin_image_awake = data['CutinImageAwake'] + ret.status_chara_icon = data['StatusCharaIcon'] + ret.status_chara_icon_awake = data['StatusCharaIconAwake'] + ret.collection_display_start_date = data['CollectionDisplayStartDate'] + ret.collection_empty_frame_display_flag = data['CollectionEmptyFrameDisplayFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.support_log_id) \ + + encode_short(self.chara_id) \ + + encode_str(self.name) \ + + encode_byte(self.rarity) \ + + encode_short(self.support_log_type_id) \ + + encode_int(self.sale_price) \ + + encode_int(self.composition_exp) \ + + encode_int(self.awakening_exp) \ + + encode_short(self.use_rank) \ + + encode_short(self.group_no) \ + + encode_short(self.adaptable_appear_append) \ + + encode_short(self.punisher_appear_append) \ + + encode_str(self.state) \ + + encode_str(self.passive_state) \ + + encode_int(self.power_default_value) \ + + encode_int(self.clt_default_value) \ + + encode_int(self.awakening_1_power) \ + + encode_int(self.awakening_1_clt) \ + + encode_int(self.awakening_2_power) \ + + encode_int(self.awakening_2_clt) \ + + encode_int(self.awakening_3_power) \ + + encode_int(self.awakening_3_clt) \ + + encode_int(self.awakening_4_power) \ + + encode_int(self.awakening_4_clt) \ + + encode_int(self.awakening_5_power) \ + + encode_int(self.awakening_5_clt) \ + + encode_int(self.normal_leader) \ + + encode_int(self.holo_leader) \ + + encode_str(self.ui_display_power_title) \ + + encode_str(self.ui_display_power_content) \ + + encode_str(self.prefab) \ + + encode_str(self.cutin_mode) \ + + encode_str(self.skill_voice_id) \ + + encode_str(self.skill_name) \ + + encode_str(self.name_in_skill_cutin) \ + + encode_str(self.skill_text) \ + + encode_str(self.skill_text_in_skill_cutin) \ + + encode_str(self.chara_info) \ + + encode_str(self.cutin_image) \ + + encode_str(self.cutin_image_awake) \ + + encode_str(self.status_chara_icon) \ + + encode_str(self.status_chara_icon_awake) \ + + encode_date_str(self.collection_display_start_date) \ + + encode_byte(self.collection_empty_frame_display_flag) + +class SupportLogTypeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.support_log_type_id = decode_short(data, off) + off += SHORT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "SupportLogTypeData": + ret = cls(b"\x00" * 99, 0) + ret.support_log_type_id = data['SupportLogTypeId'] + ret.name = data['Name'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_short(self.support_log_type_id) \ + + encode_str(self.name) + +class EpisodeAppendData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.episode_append_id = decode_int(data, off) + off += INT_OFF + + self.episode_append_type = decode_byte(data, off) + off += BYTE_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.flavor_text, new_off = decode_str(data, off) + off += new_off + + self.sale_possible_flag = decode_int(data, off) + off += INT_OFF + + self.sale_price = decode_int(data, off) + off += INT_OFF + + self.episode_append_icon, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EpisodeAppendData": + ret = cls(b"\x00" * 99, 0) + ret.episode_append_id = data['EpisodeAppendId'] + ret.episode_append_type = data['EpisodeAppendType'] + ret.name = data['Name'] + ret.flavor_text = data['FlavorText'] + ret.sale_possible_flag = data['SalePossibleFlag'] + ret.sale_price = data['SalePrice'] + ret.episode_append_icon = data['EpisodeAppendIcon'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.episode_append_id) \ + + encode_byte(self.episode_append_type) \ + + encode_str(self.name) \ + + encode_str(self.flavor_text) \ + + encode_int(self.sale_possible_flag) \ + + encode_int(self.sale_price) \ + + encode_str(self.episode_append_icon) + +class QuestDefragMatchQuestData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_quest_id = decode_int(data, off) + off += INT_OFF + + self.quest_scene_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_quest_boss_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.display_name, new_off = decode_str(data, off) + off += new_off + + self.map_no = decode_int(data, off) + off += INT_OFF + + self.unit_data1, new_off = decode_str(data, off) + off += new_off + + self.unit_data2, new_off = decode_str(data, off) + off += new_off + + self.unit_data3, new_off = decode_str(data, off) + off += new_off + + self.adaptable_rate, new_off = decode_str(data, off) + off += new_off + + self.punisher_flag = decode_byte(data, off) + off += BYTE_OFF + + self.comment_details, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestDefragMatchQuestData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_quest_id = data['DefragMatchQuestId'] + ret.quest_scene_id = data['QuestSceneId'] + ret.defrag_match_quest_boss_table_sub_id = data['DefragMatchBossTableSubId'] + ret.display_name = data['DisplayName'] + ret.map_no = data['VsModeNo'] + ret.unit_data1 = data['UnitData1'] + ret.unit_data2 = data['UnitData2'] + ret.unit_data3 = data['UnitData3'] + ret.adaptable_rate = data['AdaptableRate'] + ret.punisher_flag = data['PunisherFlag'] + ret.comment_details = data['CommentDetails'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_quest_id) \ + + encode_int(self.quest_scene_id) \ + + encode_int(self.defrag_match_quest_boss_table_sub_id) \ + + encode_str(self.display_name) \ + + encode_int(self.map_no) \ + + encode_str(self.unit_data1) \ + + encode_str(self.unit_data2) \ + + encode_str(self.unit_data3) \ + + encode_str(self.adaptable_rate) \ + + encode_byte(self.punisher_flag) \ + + encode_str(self.comment_details) + +class QuestDefragMatchQuestBossTableData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_quest_boss_table_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_quest_boss_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.wave = decode_int(data, off) + off += INT_OFF + + self.type = decode_int(data, off) + off += INT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.rate = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestDefragMatchQuestBossTableData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_quest_boss_table_id = data['DefragMatchBossTableId'] + ret.defrag_match_quest_boss_table_sub_id = data['DefragMatchBossTableSubId'] + ret.wave = data['Wave'] + ret.type = data['Type'] + ret.unit_id = data['UnitId'] + ret.rate = data['Rate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_quest_boss_table_id) \ + + encode_int(self.defrag_match_quest_boss_table_sub_id) \ + + encode_int(self.wave) \ + + encode_int(self.type) \ + + encode_int(self.unit_id) \ + + encode_int(self.rate) + +class DefragMatchData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.season_no = decode_short(data, off) + off += SHORT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.first_attack_bonus_coefficient, new_off = decode_str(data, off) + off += new_off + + self.last_attack_bonus_coefficient, new_off = decode_str(data, off) + off += new_off + + self.memo, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_id = data['DefragMatchId'] + ret.season_no = data['SeasonNo'] + ret.title = data['Title'] + ret.first_attack_bonus_coefficient = data['FirstAttackBonusCoefficient'] + ret.last_attack_bonus_coefficient = data['LastAttackBonusCoefficient'] + ret.memo = data['Memo'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_id) \ + + encode_short(self.season_no) \ + + encode_str(self.title) \ + + encode_str(self.first_attack_bonus_coefficient) \ + + encode_str(self.last_attack_bonus_coefficient) \ + + encode_str(self.memo) + +class DefragMatchSeedData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_seed_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.check_type = decode_byte(data, off) + off += BYTE_OFF + + self.need_class_num = decode_short(data, off) + off += SHORT_OFF + + self.need_cleared_trial_tower_id = decode_short(data, off) + off += SHORT_OFF + + self.get_league_point = decode_int(data, off) + off += INT_OFF + + self.get_league_score = decode_short(data, off) + off += SHORT_OFF + + self.set_class_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchSeedData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_seed_id = data['DefragMatchSeedId'] + ret.defrag_match_id = data['DefragMatchId'] + ret.check_type = data['CheckType'] + ret.need_class_num = data['NeedClassNum'] + ret.need_cleared_trial_tower_id = data['NeedClearedTrialTowerId'] + ret.get_league_point = data['GetLeaguePoint'] + ret.get_league_score = data['GetLeagueScore'] + ret.set_class_num = data['SetClassNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_seed_id) \ + + encode_int(self.defrag_match_id) \ + + encode_byte(self.check_type) \ + + encode_short(self.need_class_num) \ + + encode_short(self.need_cleared_trial_tower_id) \ + + encode_int(self.get_league_point) \ + + encode_short(self.get_league_score) \ + + encode_short(self.set_class_num) + +class DefragMatchSpecialEffectData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_special_effec_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.special_effect_type = decode_byte(data, off) + off += BYTE_OFF + + self.special_effect_coefficient, new_off = decode_str(data, off) + off += new_off + + self.target_chara_id = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchSpecialEffectData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_special_effec_id = data['DefragMatchSpecialEffectsId'] + ret.defrag_match_id = data['DefragMatchId'] + ret.special_effect_type = data['SpecialEffectType'] + ret.special_effect_coefficient = data['SpecialEffectCoefficient'] + ret.target_chara_id = data['TargetCharaId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_special_effec_id) \ + + encode_int(self.defrag_match_id) \ + + encode_byte(self.special_effect_type) \ + + encode_str(self.special_effect_coefficient) \ + + encode_short(self.target_chara_id) + +class DefragMatchGradeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_grade_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.class_num = decode_short(data, off) + off += SHORT_OFF + + self.grade_name, new_off = decode_str(data, off) + off += new_off + + self.class_name, new_off = decode_str(data, off) + off += new_off + + self.promotion_line_league_score = decode_short(data, off) + off += SHORT_OFF + + self.demotion_line_league_score = decode_short(data, off) + off += SHORT_OFF + + self.league_score_decrease_flag = decode_byte(data, off) + off += BYTE_OFF + + self.target_league_point_1 = decode_int(data, off) + off += INT_OFF + + self.target_league_point_2 = decode_int(data, off) + off += INT_OFF + + self.mvp_add_league_point = decode_int(data, off) + off += INT_OFF + + self.mob_level = decode_short(data, off) + off += SHORT_OFF + + self.normal_boss_level = decode_short(data, off) + off += SHORT_OFF + + self.adaptable_level = decode_short(data, off) + off += SHORT_OFF + + self.punisher_level = decode_short(data, off) + off += SHORT_OFF + + self.punisher_appearance_rate, new_off = decode_str(data, off) + off += new_off + + self.cpu_level = decode_short(data, off) + off += SHORT_OFF + + self.record_medal_drop_num = decode_short(data, off) + off += SHORT_OFF + + self.target_league_point_coefficient, new_off = decode_str(data, off) + off += new_off + + self.reward_table_sub_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchGradeData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_grade_id = data['DefragMatchGradeId'] + ret.defrag_match_id = data['DefragMatchId'] + ret.class_num = data['ClassNum'] + ret.grade_name = data['GradeName'] + ret.class_name = data['ClassName'] + ret.promotion_line_league_score = data['PromotionLineLeagueScore'] + ret.demotion_line_league_score = data['DemotionLineLeagueScore'] + ret.league_score_decrease_flag = data['LeagueScoreDecreaseFlag'] + ret.target_league_point_1 = data['TargetLeaguePoint1'] + ret.target_league_point_2 = data['TargetLeaguePoint2'] + ret.mvp_add_league_point = data['MvpAddLeaguePoint'] + ret.mob_level = data['MobLevel'] + ret.normal_boss_level = data['NormalBossLevel'] + ret.adaptable_level = data['AdaptableLevel'] + ret.punisher_level = data['PunisherLevel'] + ret.punisher_appearance_rate = data['PunisherAppearanceRate'] + ret.cpu_level = data['CpuLevel'] + ret.record_medal_drop_num = data['RecordMedalDropNum'] + ret.target_league_point_coefficient = data['TargetLeaguePointCoefficient'] + ret.reward_table_sub_id = data['RewardTableSubId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_grade_id) \ + + encode_int(self.defrag_match_id) \ + + encode_short(self.class_num) \ + + encode_str(self.grade_name) \ + + encode_str(self.class_name) \ + + encode_short(self.promotion_line_league_score) \ + + encode_short(self.demotion_line_league_score) \ + + encode_byte(self.league_score_decrease_flag) \ + + encode_int(self.target_league_point_1) \ + + encode_int(self.target_league_point_2) \ + + encode_int(self.mvp_add_league_point) \ + + encode_short(self.mob_level) \ + + encode_short(self.normal_boss_level) \ + + encode_short(self.adaptable_level) \ + + encode_short(self.punisher_level) \ + + encode_str(self.punisher_appearance_rate) \ + + encode_short(self.cpu_level) \ + + encode_short(self.record_medal_drop_num) \ + + encode_str(self.target_league_point_coefficient) \ + + encode_int(self.reward_table_sub_id) + +class DefragMatchCpuUnitData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_cpu_unit_id = decode_int(data, off) + off += INT_OFF + + self.appearance_start_defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.appearance_end_defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.appearance_start_class_num = decode_int(data, off) + off += INT_OFF + + self.appearance_end_class_num = decode_int(data, off) + off += INT_OFF + + self.hero_log_hero_log_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_log_level = decode_short(data, off) + off += SHORT_OFF + + self.hero_log_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.hero_log_property1_property_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_property1_value1 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property1_value2 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property2_property_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_property2_value1 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property2_value2 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property3_property_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_property3_value1 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property3_value2 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property4_property_id = decode_int(data, off) + off += INT_OFF + + self.hero_log_property4_value1 = decode_int(data, off) + off += INT_OFF + + self.hero_log_property4_value2 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_equipment_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_enhancement_value = decode_short(data, off) + off += SHORT_OFF + + self.main_weapon_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.main_weapon_property1_property_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property1_value1 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property1_value2 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property2_property_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property2_value1 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property2_value2 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property3_property_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property3_value1 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property3_value2 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property4_property_id = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property4_value1 = decode_int(data, off) + off += INT_OFF + + self.main_weapon_property4_value2 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_equipment_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_enhancement_value = decode_short(data, off) + off += SHORT_OFF + + self.sub_equipment_awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self.sub_equipment_property1_property_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property1_value1 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property1_value2 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property2_property_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property2_value1 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property2_value2 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property3_property_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property3_value1 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property3_value2 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property4_property_id = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property4_value1 = decode_int(data, off) + off += INT_OFF + + self.sub_equipment_property4_value2 = decode_int(data, off) + off += INT_OFF + + self.skill_slot1_skill_id = decode_short(data, off) + off += SHORT_OFF + + self.skill_slot2_skill_id = decode_short(data, off) + off += SHORT_OFF + + self.skill_slot3_skill_id = decode_short(data, off) + off += SHORT_OFF + + self.skill_slot4_skill_id = decode_short(data, off) + off += SHORT_OFF + + self.skill_slot5_skill_id = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchCpuUnitData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_cpu_unit_id = data['DefragMatchCpuUnitsId'] + ret.appearance_start_defrag_match_id = data['AppearanceStartDefragMatchId'] + ret.appearance_end_defrag_match_id = data['AppearanceEndDefragMatchId'] + ret.appearance_start_class_num = data['AppearanceStartClassNum'] + ret.appearance_end_class_num = data['AppearanceEndClassNum'] + ret.hero_log_hero_log_id = data['HeroLogHeroLogId'] + ret.hero_log_log_level = data['HeroLogLogLevel'] + ret.hero_log_awakening_stage = data['HeroLogAwakeningStage'] + ret.hero_log_property1_property_id = data['HeroLogProperty1PropertyId'] + ret.hero_log_property1_value1 = data['HeroLogProperty1Value1'] + ret.hero_log_property1_value2 = data['HeroLogProperty1Value2'] + ret.hero_log_property2_property_id = data['HeroLogProperty2PropertyId'] + ret.hero_log_property2_value1 = data['HeroLogProperty2Value1'] + ret.hero_log_property2_value2 = data['HeroLogProperty2Value2'] + ret.hero_log_property3_property_id = data['HeroLogProperty3PropertyId'] + ret.hero_log_property3_value1 = data['HeroLogProperty3Value1'] + ret.hero_log_property3_value2 = data['HeroLogProperty3Value2'] + ret.hero_log_property4_property_id = data['HeroLogProperty4PropertyId'] + ret.hero_log_property4_value1 = data['HeroLogProperty4Value1'] + ret.hero_log_property4_value2 = data['HeroLogProperty4Value2'] + ret.main_weapon_equipment_id = data['MainWeaponEquipmentId'] + ret.main_weapon_enhancement_value = data['MainWeaponEnhancementValue'] + ret.main_weapon_awakening_stage = data['MainWeaponAwakeningStage'] + ret.main_weapon_property1_property_id = data['MainWeaponProperty1PropertyId'] + ret.main_weapon_property1_value1 = data['MainWeaponProperty1Value1'] + ret.main_weapon_property1_value2 = data['MainWeaponProperty1Value2'] + ret.main_weapon_property2_property_id = data['MainWeaponProperty2PropertyId'] + ret.main_weapon_property2_value1 = data['MainWeaponProperty2Value1'] + ret.main_weapon_property2_value2 = data['MainWeaponProperty2Value2'] + ret.main_weapon_property3_property_id = data['MainWeaponProperty3PropertyId'] + ret.main_weapon_property3_value1 = data['MainWeaponProperty3Value1'] + ret.main_weapon_property3_value2 = data['MainWeaponProperty3Value2'] + ret.main_weapon_property4_property_id = data['MainWeaponProperty4PropertyId'] + ret.main_weapon_property4_value1 = data['MainWeaponProperty4Value1'] + ret.main_weapon_property4_value2 = data['MainWeaponProperty4Value2'] + ret.sub_equipment_equipment_id = data['SubEquipmentEquipmentId'] + ret.sub_equipment_enhancement_value = data['SubEquipmentEnhancementValue'] + ret.sub_equipment_awakening_stage = data['SubEquipmentAwakeningStage'] + ret.sub_equipment_property1_property_id = data['SubEquipmentProperty1PropertyId'] + ret.sub_equipment_property1_value1 = data['SubEquipmentProperty1Value1'] + ret.sub_equipment_property1_value2 = data['SubEquipmentProperty1Value2'] + ret.sub_equipment_property2_property_id = data['SubEquipmentProperty2PropertyId'] + ret.sub_equipment_property2_value1 = data['SubEquipmentProperty2Value1'] + ret.sub_equipment_property2_value2 = data['SubEquipmentProperty2Value2'] + ret.sub_equipment_property3_property_id = data['SubEquipmentProperty3PropertyId'] + ret.sub_equipment_property3_value1 = data['SubEquipmentProperty3Value1'] + ret.sub_equipment_property3_value2 = data['SubEquipmentProperty3Value2'] + ret.sub_equipment_property4_property_id = data['SubEquipmentProperty4PropertyId'] + ret.sub_equipment_property4_value1 = data['SubEquipmentProperty4Value1'] + ret.sub_equipment_property4_value2 = data['SubEquipmentProperty4Value2'] + ret.skill_slot1_skill_id = data['SkillSlot1SkillId'] + ret.skill_slot2_skill_id = data['SkillSlot2SkillId'] + ret.skill_slot3_skill_id = data['SkillSlot3SkillId'] + ret.skill_slot4_skill_id = data['SkillSlot4SkillId'] + ret.skill_slot5_skill_id = data['SkillSlot5SkillId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_cpu_unit_id) \ + + encode_int(self.appearance_start_defrag_match_id) \ + + encode_int(self.appearance_end_defrag_match_id) \ + + encode_int(self.appearance_start_class_num) \ + + encode_int(self.appearance_end_class_num) \ + + encode_int(self.hero_log_hero_log_id) \ + + encode_short(self.hero_log_log_level) \ + + encode_short(self.hero_log_awakening_stage) \ + + encode_int(self.hero_log_property1_property_id) \ + + encode_int(self.hero_log_property1_value1) \ + + encode_int(self.hero_log_property1_value2) \ + + encode_int(self.hero_log_property2_property_id) \ + + encode_int(self.hero_log_property2_value1) \ + + encode_int(self.hero_log_property2_value2) \ + + encode_int(self.hero_log_property3_property_id) \ + + encode_int(self.hero_log_property3_value1) \ + + encode_int(self.hero_log_property3_value2) \ + + encode_int(self.hero_log_property4_property_id) \ + + encode_int(self.hero_log_property4_value1) \ + + encode_int(self.hero_log_property4_value2) \ + + encode_int(self.main_weapon_equipment_id) \ + + encode_short(self.main_weapon_enhancement_value) \ + + encode_short(self.main_weapon_awakening_stage) \ + + encode_int(self.main_weapon_property1_property_id) \ + + encode_int(self.main_weapon_property1_value1) \ + + encode_int(self.main_weapon_property1_value2) \ + + encode_int(self.main_weapon_property2_property_id) \ + + encode_int(self.main_weapon_property2_value1) \ + + encode_int(self.main_weapon_property2_value2) \ + + encode_int(self.main_weapon_property3_property_id) \ + + encode_int(self.main_weapon_property3_value1) \ + + encode_int(self.main_weapon_property3_value2) \ + + encode_int(self.main_weapon_property4_property_id) \ + + encode_int(self.main_weapon_property4_value1) \ + + encode_int(self.main_weapon_property4_value2) \ + + encode_int(self.sub_equipment_equipment_id) \ + + encode_short(self.sub_equipment_enhancement_value) \ + + encode_short(self.sub_equipment_awakening_stage) \ + + encode_int(self.sub_equipment_property1_property_id) \ + + encode_int(self.sub_equipment_property1_value1) \ + + encode_int(self.sub_equipment_property1_value2) \ + + encode_int(self.sub_equipment_property2_property_id) \ + + encode_int(self.sub_equipment_property2_value1) \ + + encode_int(self.sub_equipment_property2_value2) \ + + encode_int(self.sub_equipment_property3_property_id) \ + + encode_int(self.sub_equipment_property3_value1) \ + + encode_int(self.sub_equipment_property3_value2) \ + + encode_int(self.sub_equipment_property4_property_id) \ + + encode_int(self.sub_equipment_property4_value1) \ + + encode_int(self.sub_equipment_property4_value2) \ + + encode_short(self.skill_slot1_skill_id) \ + + encode_short(self.skill_slot2_skill_id) \ + + encode_short(self.skill_slot3_skill_id) \ + + encode_short(self.skill_slot4_skill_id) \ + + encode_short(self.skill_slot5_skill_id) + +class DefragMatchCpuSupportLogData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_cpu_support_log_id = decode_int(data, off) + off += INT_OFF + + self.appearance_start_defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.appearance_end_defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.appearance_start_class_num = decode_int(data, off) + off += INT_OFF + + self.appearance_end_class_num = decode_int(data, off) + off += INT_OFF + + self.support_log_id = decode_int(data, off) + off += INT_OFF + + self.awakening_stage = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchCpuSupportLogData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_cpu_support_log_id = data['DefragMatchCpuSupportLogId'] + ret.appearance_start_defrag_match_id = data['AppearanceStartDefragMatchId'] + ret.appearance_end_defrag_match_id = data['AppearanceEndDefragMatchId'] + ret.appearance_start_class_num = data['AppearanceStartClassNum'] + ret.appearance_end_class_num = data['AppearanceEndClassNum'] + ret.support_log_id = data['SupportLogId'] + ret.awakening_stage = data['AwakeningStage'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_cpu_support_log_id) \ + + encode_int(self.appearance_start_defrag_match_id) \ + + encode_int(self.appearance_end_defrag_match_id) \ + + encode_int(self.appearance_start_class_num) \ + + encode_int(self.appearance_end_class_num) \ + + encode_int(self.support_log_id) \ + + encode_short(self.awakening_stage) + +class DefragMatchPeriodBonusData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_period_bonus_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.bonus_type = decode_byte(data, off) + off += BYTE_OFF + + self.param_1, new_off = decode_str(data, off) + off += new_off + + self.param_2, new_off = decode_str(data, off) + off += new_off + + self.param_3, new_off = decode_str(data, off) + off += new_off + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchPeriodBonusData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_period_bonus_id = data['DefragMatchPeriodBonusId'] + ret.defrag_match_id = data['DefragMatchId'] + ret.bonus_type = data['BonusType'] + ret.param_1 = data['Param1'] + ret.param_2 = data['Param2'] + ret.param_3 = data['Param3'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_period_bonus_id) \ + + encode_int(self.defrag_match_id) \ + + encode_byte(self.bonus_type) \ + + encode_str(self.param_1) \ + + encode_str(self.param_2) \ + + encode_str(self.param_3) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class DefragMatchRandomBonusTableData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_random_bonus_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_random_bonus_condition_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.condition_value1 = decode_int(data, off) + off += INT_OFF + + self.condition_value2 = decode_int(data, off) + off += INT_OFF + + self.get_league_point = decode_int(data, off) + off += INT_OFF + + self.random_bonus_num = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchRandomBonusTableData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_random_bonus_id = data['DefragMatchRandomBonusId'] + ret.defrag_match_id = data['DefragMatchId'] + ret.defrag_match_random_bonus_condition_id = data['DefragMatchRandomBonusConditionId'] + ret.name = data['Name'] + ret.condition_value1 = data['ConditionValue1'] + ret.condition_value2 = data['ConditionValue2'] + ret.get_league_point = data['GetLeaguePoint'] + ret.random_bonus_num = data['RandomBonusNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_random_bonus_id) \ + + encode_int(self.defrag_match_id) \ + + encode_int(self.defrag_match_random_bonus_condition_id) \ + + encode_str(self.name) \ + + encode_int(self.condition_value1) \ + + encode_int(self.condition_value2) \ + + encode_int(self.get_league_point) \ + + encode_byte(self.random_bonus_num) + +class DefragMatchRandomBonusConditionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_random_bonus_condition_id = decode_int(data, off) + off += INT_OFF + + self.format, new_off = decode_str(data, off) + off += new_off + + self.hud_format, new_off = decode_str(data, off) + off += new_off + + self.format_param_size = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchRandomBonusConditionData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_random_bonus_condition_id = data['DefragMatchRandomBonusConditionId'] + ret.format = data['Format'] + ret.hud_format = data['HudFormat'] + ret.format_param_size = data['FormatParamSize'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_random_bonus_condition_id) \ + + encode_str(self.format) \ + + encode_str(self.hud_format) \ + + encode_int(self.format_param_size) + +class DefragMatchRareDropData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.defrag_match_rare_drop_id = decode_int(data, off) + off += INT_OFF + + self.defrag_match_id = decode_int(data, off) + off += INT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_int(data, off) + off += INT_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_int(data, off) + off += INT_OFF + + self.strength_min = decode_int(data, off) + off += INT_OFF + + self.strength_max = decode_int(data, off) + off += INT_OFF + + self.property_table_sub_id = decode_int(data, off) + off += INT_OFF + + self.drop_rate, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "DefragMatchRareDropData": + ret = cls(b"\x00" * 99, 0) + ret.defrag_match_rare_drop_id = data['DefragMatchRareDropId'] + ret.defrag_match_id = data['DefragMatchId'] + ret.unit_id = data['UnitId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength_min = data['StrengthMin'] + ret.strength_max = data['StrengthMax'] + ret.property_table_sub_id = data['PropertyTableSubId'] + ret.drop_rate = data['DropRate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.defrag_match_rare_drop_id) \ + + encode_int(self.defrag_match_id) \ + + encode_int(self.unit_id) \ + + encode_int(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_int(self.common_reward_num) \ + + encode_int(self.strength_min) \ + + encode_int(self.strength_max) \ + + encode_int(self.property_table_sub_id) \ + + encode_str(self.drop_rate) + +class YuiMedalShopData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.yui_medal_shop_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.description, new_off = decode_str(data, off) + off += new_off + + self.selling_yui_medal = decode_short(data, off) + off += SHORT_OFF + + self.selling_col = decode_int(data, off) + off += INT_OFF + + self.selling_event_item_id = decode_int(data, off) + off += INT_OFF + + self.selling_event_item_num = decode_int(data, off) + off += INT_OFF + + self.selling_ticket_num = decode_int(data, off) + off += INT_OFF + + self.purchase_limit = decode_short(data, off) + off += SHORT_OFF + + self.pick_up_flag = decode_byte(data, off) + off += BYTE_OFF + + self.product_category = decode_byte(data, off) + off += BYTE_OFF + + self.sales_type = decode_byte(data, off) + off += BYTE_OFF + + self.target_days = decode_byte(data, off) + off += BYTE_OFF + + self.target_hour = decode_byte(data, off) + off += BYTE_OFF + + self.interval_hour = decode_byte(data, off) + off += BYTE_OFF + + self.sales_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.sales_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.sort = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "YuiMedalShopData": + ret = cls(b"\x00" * 99, 0) + ret.yui_medal_shop_id = data['YuiMedalShopId'] + ret.name = data['Name'] + ret.description = data['Description'] + ret.selling_yui_medal = data['SellingYuiMedal'] + ret.selling_col = data['SellingCol'] + ret.selling_event_item_id = data['SellingEventItemId'] + ret.selling_event_item_num = data['SellingEventItemNum'] + ret.selling_ticket_num = data['SellingTicketNum'] + ret.purchase_limit = data['PurchaseLimit'] + ret.pick_up_flag = data['PickUpFlag'] + ret.product_category = data['ProductCategory'] + ret.sales_type = data['SalesType'] + ret.target_days = data['TargetDays'] + ret.target_hour = data['TargetHour'] + ret.interval_hour = data['IntervalHour'] + ret.sales_start_date = data['SalesStartDate'] + ret.sales_end_date = data['SalesEndDate'] + ret.sort = data['Sort'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.yui_medal_shop_id) \ + + encode_str(self.name) \ + + encode_str(self.description) \ + + encode_short(self.selling_yui_medal) \ + + encode_int(self.selling_col) \ + + encode_int(self.selling_event_item_id) \ + + encode_int(self.selling_event_item_num) \ + + encode_int(self.selling_ticket_num) \ + + encode_short(self.purchase_limit) \ + + encode_byte(self.pick_up_flag) \ + + encode_byte(self.product_category) \ + + encode_byte(self.sales_type) \ + + encode_byte(self.target_days) \ + + encode_byte(self.target_hour) \ + + encode_byte(self.interval_hour) \ + + encode_date_str(self.sales_start_date) \ + + encode_date_str(self.sales_end_date) \ + + encode_byte(self.sort) + +class YuiMedalShopItemData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.yui_medal_shop_item_id = decode_int(data, off) + off += INT_OFF + + self.yui_medal_shop_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "YuiMedalShopItemData": + ret = cls(b"\x00" * 99, 0) + ret.yui_medal_shop_item_id = data['YuiMedalShopItemId'] + ret.yui_medal_shop_id = data['YuiMedalShopId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.yui_medal_shop_item_id) \ + + encode_int(self.yui_medal_shop_id) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class EventSceneData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.event_scene_id = decode_int(data, off) + off += INT_OFF + + self.event_id = decode_int(data, off) + off += INT_OFF + + self.scene_type = decode_byte(data, off) + off += BYTE_OFF + + self.episode_no = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.param_1, new_off = decode_str(data, off) + off += new_off + + self.param_2, new_off = decode_str(data, off) + off += new_off + + self.param_3, new_off = decode_str(data, off) + off += new_off + + self.adv_name, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EventSceneData": + ret = cls(b"\x00" * 99, 0) + ret.event_scene_id = data['EventSceneId'] + ret.event_id = data['EventId'] + ret.scene_type = data['SceneType'] + ret.episode_no = data['EpisodeNo'] + ret.title = data['Title'] + ret.param_1 = data['Param1'] + ret.param_2 = data['Param2'] + ret.param_3 = data['Param3'] + ret.adv_name = data['AdvName'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.event_scene_id) \ + + encode_int(self.event_id) \ + + encode_byte(self.scene_type) \ + + encode_int(self.episode_no) \ + + encode_str(self.title) \ + + encode_str(self.param_1) \ + + encode_str(self.param_2) \ + + encode_str(self.param_3) \ + + encode_str(self.adv_name) + +class GenericCampaignPeriodData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.generic_campaign_period_id = decode_int(data, off) + off += INT_OFF + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GenericCampaignPeriodData": + ret = cls(b"\x00" * 99, 0) + ret.generic_campaign_period_id = data['GenericCampaignPeriodId'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.generic_campaign_period_id) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class BeginnerMissionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BeginnerMissionData": + ret = cls(b"\x00" * 99, 0) + ret.beginner_mission_id = data['BeginnerMissionId'] + ret.name = data['Name'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.beginner_mission_id) \ + + encode_str(self.name) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class BeginnerMissionConditionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.beginner_mission_condition_id = decode_int(data, off) + off += INT_OFF + + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + + self.seat_num = decode_short(data, off) + off += SHORT_OFF + + self.mission_num = decode_short(data, off) + off += SHORT_OFF + + self.display_content, new_off = decode_str(data, off) + off += new_off + + self.display_tips, new_off = decode_str(data, off) + off += new_off + + self.condition_type = decode_byte(data, off) + off += BYTE_OFF + + self.condition_param_1, new_off = decode_str(data, off) + off += new_off + + self.condition_param_2, new_off = decode_str(data, off) + off += new_off + + self.condition_param_3, new_off = decode_str(data, off) + off += new_off + + self.required_achievement_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BeginnerMissionConditionData": + ret = cls(b"\x00" * 99, 0) + ret.beginner_mission_condition_id = data['BeginnerMissionConditionId'] + ret.beginner_mission_id = data['BeginnerMissionId'] + ret.seat_num = data['SeatNum'] + ret.mission_num = data['MissionNum'] + ret.display_content = data['DisplayContent'] + ret.display_tips = data['DisplayTips'] + ret.condition_type = data['ConditionType'] + ret.condition_param_1 = data['ConditionParam1'] + ret.condition_param_2 = data['ConditionParam2'] + ret.condition_param_3 = data['ConditionParam3'] + ret.required_achievement_num = data['RequiredAchievementNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.beginner_mission_condition_id) \ + + encode_int(self.beginner_mission_id) \ + + encode_short(self.seat_num) \ + + encode_short(self.mission_num) \ + + encode_str(self.display_content) \ + + encode_str(self.display_tips) \ + + encode_byte(self.condition_type) \ + + encode_str(self.condition_param_1) \ + + encode_str(self.condition_param_2) \ + + encode_str(self.condition_param_3) \ + + encode_short(self.required_achievement_num) + +class BeginnerMissionRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + + self.beginner_mission_condition_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BeginnerMissionRewardData": + ret = cls(b"\x00" * 99, 0) + ret.beginner_mission_id = data['BeginnerMissionId'] + ret.beginner_mission_condition_id = data['BeginnerMissionConditionId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.beginner_mission_id) \ + + encode_int(self.beginner_mission_condition_id) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class BeginnerMissionSeatConditionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + + self.beginner_mission_seat_condition_id = decode_int(data, off) + off += INT_OFF + + self.seat_num = decode_short(data, off) + off += SHORT_OFF + + self.mission_seat_num = decode_short(data, off) + off += SHORT_OFF + + self.display_content, new_off = decode_str(data, off) + off += new_off + + self.display_tips, new_off = decode_str(data, off) + off += new_off + + self.condition_type = decode_byte(data, off) + off += BYTE_OFF + + self.condition_param_1, new_off = decode_str(data, off) + off += new_off + + self.condition_param_2, new_off = decode_str(data, off) + off += new_off + + self.condition_param_3, new_off = decode_str(data, off) + off += new_off + + self.required_achievement_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BeginnerMissionSeatConditionData": + ret = cls(b"\x00" * 99, 0) + ret.beginner_mission_id = data['BeginnerMissionId'] + ret.beginner_mission_seat_condition_id = data['BeginnerMissionSeatConditionId'] + ret.seat_num = data['SeatNum'] + ret.mission_seat_num = data['MissionSeatNum'] + ret.display_content = data['DisplayContent'] + ret.display_tips = data['DisplayTips'] + ret.condition_type = data['ConditionType'] + ret.condition_param_1 = data['ConditionParam1'] + ret.condition_param_2 = data['ConditionParam2'] + ret.condition_param_3 = data['ConditionParam3'] + ret.required_achievement_num = data['RequiredAchievementNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.beginner_mission_id) \ + + encode_int(self.beginner_mission_seat_condition_id) \ + + encode_short(self.seat_num) \ + + encode_short(self.mission_seat_num) \ + + encode_str(self.display_content) \ + + encode_str(self.display_tips) \ + + encode_byte(self.condition_type) \ + + encode_str(self.condition_param_1) \ + + encode_str(self.condition_param_2) \ + + encode_str(self.condition_param_3) \ + + encode_short(self.required_achievement_num) + +class BeginnerMissionSeatRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.beginner_mission_id = decode_int(data, off) + off += INT_OFF + + self.beginner_mission_seat_condition_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "BeginnerMissionSeatRewardData": + ret = cls(b"\x00" * 99, 0) + ret.beginner_mission_id = data['BeginnerMissionId'] + ret.beginner_mission_seat_condition_id = data['BeginnerMissionSeatConditionId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.beginner_mission_id) \ + + encode_int(self.beginner_mission_seat_condition_id) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class EventItemData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.event_item_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.flavor_text, new_off = decode_str(data, off) + off += new_off + + self.event_item_icon, new_off = decode_str(data, off) + off += new_off + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EventItemData": + ret = cls(b"\x00" * 99, 0) + ret.event_item_id = data['EventItemId'] + ret.name = data['Name'] + ret.flavor_text = data['FlavorText'] + ret.event_item_icon = data['EventItemIcon'] + ret.unit_id = data['UnitId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.event_item_id) \ + + encode_str(self.name) \ + + encode_str(self.flavor_text) \ + + encode_str(self.event_item_icon) \ + + encode_int(self.unit_id) + +class EventMonsterData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.event_monster_id = decode_int(data, off) + off += INT_OFF + + self.event_id = decode_int(data, off) + off += INT_OFF + + self.unit_id = decode_int(data, off) + off += INT_OFF + + self.event_item_id = decode_int(data, off) + off += INT_OFF + + self.drop_rate, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "EventMonsterData": + ret = cls(b"\x00" * 99, 0) + ret.event_monster_id = data['EventMonsterId'] + ret.event_id = data['EventId'] + ret.unit_id = data['UnitId'] + ret.event_item_id = data['EventItemId'] + ret.drop_rate = data['DropRate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.event_monster_id) \ + + encode_int(self.event_id) \ + + encode_int(self.unit_id) \ + + encode_int(self.event_item_id) \ + + encode_str(self.drop_rate) + +class YuiMedalBonusData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.yui_medal_bonus_id = decode_int(data, off) + off += INT_OFF + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "YuiMedalBonusData": + ret = cls(b"\x00" * 99, 0) + ret.yui_medal_bonus_id = data['YuiMedalBonusId'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.yui_medal_bonus_id) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class YuiMedalBonusConditionData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.yui_medal_bonus_condition_id = decode_int(data, off) + off += INT_OFF + + self.yui_medal_bonus_id = decode_int(data, off) + off += INT_OFF + + self.target_days = decode_int(data, off) + off += INT_OFF + + self.get_num = decode_int(data, off) + off += INT_OFF + + self.loop_flag = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "YuiMedalBonusConditionData": + ret = cls(b"\x00" * 99, 0) + ret.yui_medal_bonus_condition_id = data['YuiMedalBonusConditionId'] + ret.yui_medal_bonus_id = data['YuiMedalBonusId'] + ret.target_days = data['TargetDays'] + ret.get_num = data['GetNum'] + ret.loop_flag = data['LoopFlag'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.yui_medal_bonus_condition_id) \ + + encode_int(self.yui_medal_bonus_id) \ + + encode_int(self.target_days) \ + + encode_int(self.get_num) \ + + encode_byte(self.loop_flag) + +class GashaMedalData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_medal_id = decode_int(data, off) + off += INT_OFF + + self.gasha_medal_type = decode_byte(data, off) + off += BYTE_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaMedalData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_medal_id = data['GashaMedalId'] + ret.gasha_medal_type = data['GashaMedalType'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_medal_id) \ + + encode_byte(self.gasha_medal_type) + +class GashaMedalTypeData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_medal_type = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.medal_icon, new_off = decode_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaMedalTypeData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_medal_type = data['GashaMedalType'] + ret.name = data['Name'] + ret.medal_icon = data['MedalIcon'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_medal_type) \ + + encode_str(self.name) \ + + encode_str(self.medal_icon) + +class GashaMedalSettingData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_id = decode_int(data, off) + off += INT_OFF + + self.gasha_medal_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaMedalSettingData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_id = data['GashaId'] + ret.gasha_medal_id = data['GashaMedalId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_id) \ + + encode_int(self.gasha_medal_id) + +class GashaMedalBonusData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.analyze_amount = decode_byte(data, off) + off += BYTE_OFF + + self.get_gasha_medal_num = decode_int(data, off) + off += INT_OFF + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaMedalBonusData": + ret = cls(b"\x00" * 99, 0) + ret.analyze_amount = data['AnalyzeAmount'] + ret.get_gasha_medal_num = data['GetGashaMedalNum'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_byte(self.analyze_amount) \ + + encode_int(self.get_gasha_medal_num) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class GashaMedalShopData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_medal_shop_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.gasha_medal_id = decode_int(data, off) + off += INT_OFF + + self.use_gasha_medal_num = decode_int(data, off) + off += INT_OFF + + self.purchase_limit = decode_short(data, off) + off += SHORT_OFF + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaMedalShopData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_medal_shop_id = data['GashaMedalShopId'] + ret.name = data['Name'] + ret.gasha_medal_id = data['GashaMedalId'] + ret.use_gasha_medal_num = data['UseGashaMedalNum'] + ret.purchase_limit = data['PurchaseLimit'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_medal_shop_id) \ + + encode_str(self.name) \ + + encode_int(self.gasha_medal_id) \ + + encode_int(self.use_gasha_medal_num) \ + + encode_short(self.purchase_limit) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class GashaMedalShopItemData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_medal_shop_item_id = decode_int(data, off) + off += INT_OFF + + self.gasha_medal_shop_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaMedalShopItemData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_medal_shop_item_id = data['GashaMedalShopItemId'] + ret.gasha_medal_shop_id = data['GashaMedalShopId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_medal_shop_item_id) \ + + encode_int(self.gasha_medal_shop_id) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class ResEarnCampaignApplicationData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.res_earn_campaign_application_id = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.posting_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.posting_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.news_id, new_off = decode_str(data, off) + off += new_off + + self.help_id = decode_int(data, off) + off += INT_OFF + + self.ad_banner_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ResEarnCampaignApplicationData": + ret = cls(b"\x00" * 99, 0) + ret.res_earn_campaign_application_id = data['ResEarnCampaignApplicationId'] + ret.title = data['Title'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + ret.posting_start_date = data['PostingStartDate'] + ret.posting_end_date = data['PostingEndDate'] + ret.news_id = data['NewsId'] + ret.help_id = data['HelpId'] + ret.ad_banner_id = data['AdBannerId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.res_earn_campaign_application_id) \ + + encode_str(self.title) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) \ + + encode_date_str(self.posting_start_date) \ + + encode_date_str(self.posting_end_date) \ + + encode_str(self.news_id) \ + + encode_int(self.help_id) \ + + encode_int(self.ad_banner_id) + +class ResEarnCampaignApplicationProductData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.res_earn_campaign_application_product_id = decode_int(data, off) + off += INT_OFF + + self.res_earn_campaign_application_id = decode_int(data, off) + off += INT_OFF + + self.award_name, new_off = decode_str(data, off) + off += new_off + + self.name, new_off = decode_str(data, off) + off += new_off + + self.need_application_point = decode_short(data, off) + off += SHORT_OFF + + self.winning_num = decode_short(data, off) + off += SHORT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ResEarnCampaignApplicationProductData": + ret = cls(b"\x00" * 99, 0) + ret.res_earn_campaign_application_product_id = data['ResEarnCampaignApplicationProductId'] + ret.res_earn_campaign_application_id = data['ResEarnCampaignApplicationId'] + ret.award_name = data['AwardName'] + ret.name = data['Name'] + ret.need_application_point = data['NeedApplicationPoint'] + ret.winning_num = data['WinningNum'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.res_earn_campaign_application_product_id) \ + + encode_int(self.res_earn_campaign_application_id) \ + + encode_str(self.award_name) \ + + encode_str(self.name) \ + + encode_short(self.need_application_point) \ + + encode_short(self.winning_num) + +class ResEarnCampaignShopData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.res_earn_campaign_shop_id = decode_int(data, off) + off += INT_OFF + + self.res_earn_campaign_application_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.selling_yui_medal = decode_short(data, off) + off += SHORT_OFF + + self.selling_col = decode_int(data, off) + off += INT_OFF + + self.selling_event_item_id = decode_int(data, off) + off += INT_OFF + + self.selling_event_item_num = decode_int(data, off) + off += INT_OFF + + self.purchase_limit = decode_short(data, off) + off += SHORT_OFF + + self.get_application_point = decode_short(data, off) + off += SHORT_OFF + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ResEarnCampaignShopData": + ret = cls(b"\x00" * 99, 0) + ret.res_earn_campaign_shop_id = data['ResEarnCampaignShopId'] + ret.res_earn_campaign_application_id = data['ResEarnCampaignApplicationId'] + ret.name = data['Name'] + ret.selling_yui_medal = data['SellingYuiMedal'] + ret.selling_col = data['SellingCol'] + ret.selling_event_item_id = data['SellingEventItemId'] + ret.selling_event_item_num = data['SellingEventItemNum'] + ret.purchase_limit = data['PurchaseLimit'] + ret.get_application_point = data['GetApplicationPoint'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.res_earn_campaign_shop_id) \ + + encode_int(self.res_earn_campaign_application_id) \ + + encode_str(self.name) \ + + encode_short(self.selling_yui_medal) \ + + encode_int(self.selling_col) \ + + encode_int(self.selling_event_item_id) \ + + encode_int(self.selling_event_item_num) \ + + encode_short(self.purchase_limit) \ + + encode_short(self.get_application_point) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class ResEarnCampaignShopItemData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.res_earn_campaign_shop_item_id = decode_int(data, off) + off += INT_OFF + + self.res_earn_campaign_shop_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "ResEarnCampaignShopItemData": + ret = cls(b"\x00" * 99, 0) + ret.res_earn_campaign_shop_item_id = data['ResEarnCampaignShopItemId'] + ret.res_earn_campaign_shop_id = data['ResEarnCampaignShopId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.res_earn_campaign_shop_item_id) \ + + encode_int(self.res_earn_campaign_shop_id) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class PayingYuiMedalBonusData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.paying_yui_medal_bonus_id = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.reward_yui_medal_num = decode_int(data, off) + off += INT_OFF + + self.news_id, new_off = decode_str(data, off) + off += new_off + + self.help_id = decode_int(data, off) + off += INT_OFF + + self.ad_banner_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "PayingYuiMedalBonusData": + ret = cls(b"\x00" * 99, 0) + ret.paying_yui_medal_bonus_id = data['PayingYuiMedalBonusId'] + ret.title = data['Title'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + ret.reward_yui_medal_num = data['RewardYuiMedalNum'] + ret.news_id = data['NewsId'] + ret.help_id = data['HelpId'] + ret.ad_banner_id = data['AdBannerId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.paying_yui_medal_bonus_id) \ + + encode_str(self.title) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) \ + + encode_int(self.reward_yui_medal_num) \ + + encode_str(self.news_id) \ + + encode_int(self.help_id) \ + + encode_int(self.ad_banner_id) + +class AcLoginBonusData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.ac_login_bonus_id = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.reward_set_sub_id = decode_int(data, off) + off += INT_OFF + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "AcLoginBonusData": + ret = cls(b"\x00" * 99, 0) + ret.ac_login_bonus_id = data['AcLoginBonusId'] + ret.title = data['Title'] + ret.reward_set_sub_id = data['RewardSetSubId'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.ac_login_bonus_id) \ + + encode_str(self.title) \ + + encode_int(self.reward_set_sub_id) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) + +class PlayCampaignData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.play_campaign_id = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.campaign_type = decode_byte(data, off) + off += BYTE_OFF + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.posting_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.posting_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.news_id, new_off = decode_str(data, off) + off += new_off + + self.help_id = decode_int(data, off) + off += INT_OFF + + self.ad_banner_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "PlayCampaignData": + ret = cls(b"\x00" * 99, 0) + ret.play_campaign_id = data['PlayCampaignId'] + ret.title = data['Title'] + ret.campaign_type = data['CampaignType'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + ret.posting_start_date = data['PostingStartDate'] + ret.posting_end_date = data['PostingEndDate'] + ret.news_id = data['NewsId'] + ret.help_id = data['HelpId'] + ret.ad_banner_id = data['AdBannerId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.play_campaign_id) \ + + encode_str(self.title) \ + + encode_byte(self.campaign_type) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) \ + + encode_date_str(self.posting_start_date) \ + + encode_date_str(self.posting_end_date) \ + + encode_str(self.news_id) \ + + encode_int(self.help_id) \ + + encode_int(self.ad_banner_id) + +class PlayCampaignRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.play_campaign_reward_id = decode_int(data, off) + off += INT_OFF + + self.play_campaign_id = decode_int(data, off) + off += INT_OFF + + self.product_no = decode_int(data, off) + off += INT_OFF + + self.product_name, new_off = decode_str(data, off) + off += new_off + + self.product_type = decode_byte(data, off) + off += BYTE_OFF + + self.resource_type = decode_byte(data, off) + off += BYTE_OFF + + self.required_credit_num = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self.title_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "PlayCampaignRewardData": + ret = cls(b"\x00" * 99, 0) + ret.play_campaign_reward_id = data['PlayCampaignRewardId'] + ret.play_campaign_id = data['PlayCampaignId'] + ret.product_no = data['ProductNo'] + ret.product_name = data['ProductName'] + ret.product_type = data['ProductType'] + ret.resource_type = data['ResourceType'] + ret.required_credit_num = data['RequiredCreditNum'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + ret.title_id = data['TitleId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.play_campaign_reward_id) \ + + encode_int(self.play_campaign_id) \ + + encode_int(self.product_no) \ + + encode_str(self.product_name) \ + + encode_byte(self.product_type) \ + + encode_byte(self.resource_type) \ + + encode_int(self.required_credit_num) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) \ + + encode_int(self.title_id) + +class GashaFreeCampaignData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.gasha_free_campaign_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.start_date, new_off = decode_date_str(data, off) + off += new_off + + self.end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "GashaFreeCampaignData": + ret = cls(b"\x00" * 99, 0) + ret.gasha_free_campaign_id = data['GashaFreeCampaignId'] + ret.name = data['Name'] + ret.start_date = data['StartDate'] + ret.end_date = data['EndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.gasha_free_campaign_id) \ + + encode_str(self.name) \ + + encode_date_str(self.start_date) \ + + encode_date_str(self.end_date) + +class QuestDropBoostCampaignData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.quest_drop_boost_campaign_id = decode_int(data, off) + off += INT_OFF + + self.name, new_off = decode_str(data, off) + off += new_off + + self.consume_ticket_num = decode_int(data, off) + off += INT_OFF + + self.drop_magnification = decode_byte(data, off) + off += BYTE_OFF + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "QuestDropBoostCampaignData": + ret = cls(b"\x00" * 99, 0) + ret.quest_drop_boost_campaign_id = data['QuestDropBoostCampaignId'] + ret.name = data['Name'] + ret.consume_ticket_num = data['ConsumeTicketNum'] + ret.drop_magnification = data['DropMagnification'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.quest_drop_boost_campaign_id) \ + + encode_str(self.name) \ + + encode_int(self.consume_ticket_num) \ + + encode_byte(self.drop_magnification) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) + +class FirstTicketPurchaseCampaignData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.first_ticket_purchase_campaign_id = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.not_target_applying_base_date, new_off = decode_date_str(data, off) + off += new_off + + self.target_ticket_purchase_id_list, new_off = decode_str(data, off) + off += new_off + + self.news_id, new_off = decode_str(data, off) + off += new_off + + self.help_id = decode_int(data, off) + off += INT_OFF + + self.ad_banner_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "FirstTicketPurchaseCampaignData": + ret = cls(b"\x00" * 99, 0) + ret.first_ticket_purchase_campaign_id = data['FirstTicketPurchaseCampaignId'] + ret.title = data['Title'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + ret.not_target_applying_base_date = data['NotTargetApplyingBaseDate'] + ret.target_ticket_purchase_id_list = data['TargetTicketPurchaseIdList'] + ret.news_id = data['NewsId'] + ret.help_id = data['HelpId'] + ret.ad_banner_id = data['AdBannerId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.first_ticket_purchase_campaign_id) \ + + encode_str(self.title) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) \ + + encode_date_str(self.not_target_applying_base_date) \ + + encode_str(self.target_ticket_purchase_id_list) \ + + encode_str(self.news_id) \ + + encode_int(self.help_id) \ + + encode_int(self.ad_banner_id) + +class LinkedSiteRegCampaignsData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.linked_site_reg_campaign_id = decode_int(data, off) + off += INT_OFF + + self.title, new_off = decode_str(data, off) + off += new_off + + self.open_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.open_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.posting_start_date, new_off = decode_date_str(data, off) + off += new_off + + self.posting_end_date, new_off = decode_date_str(data, off) + off += new_off + + self.news_id, new_off = decode_str(data, off) + off += new_off + + self.help_id = decode_int(data, off) + off += INT_OFF + + self.ad_banner_id = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "LinkedSiteRegCampaignsData": + ret = cls(b"\x00" * 99, 0) + ret.linked_site_reg_campaign_id = data['LinkedSiteRegCampaignId'] + ret.title = data['Title'] + ret.open_start_date = data['OpenStartDate'] + ret.open_end_date = data['OpenEndDate'] + ret.posting_start_date = data['PostingStartDate'] + ret.posting_end_date = data['PostingEndDate'] + ret.news_id = data['NewsId'] + ret.help_id = data['HelpId'] + ret.ad_banner_id = data['AdBannerId'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.linked_site_reg_campaign_id) \ + + encode_str(self.title) \ + + encode_date_str(self.open_start_date) \ + + encode_date_str(self.open_end_date) \ + + encode_date_str(self.posting_start_date) \ + + encode_date_str(self.posting_end_date) \ + + encode_str(self.news_id) \ + + encode_int(self.help_id) \ + + encode_int(self.ad_banner_id) + +class LinkedSiteRegCampaignRewardData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.linked_site_reg_campaign_reward_id = decode_int(data, off) + off += INT_OFF + + self.linked_site_reg_campaign_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_type = decode_byte(data, off) + off += BYTE_OFF + + self.common_reward_id = decode_int(data, off) + off += INT_OFF + + self.common_reward_num = decode_short(data, off) + off += SHORT_OFF + + self.strength = decode_int(data, off) + off += INT_OFF + + self.property1_property_id = decode_int(data, off) + off += INT_OFF + + self.property1_value1 = decode_int(data, off) + off += INT_OFF + + self.property1_value2 = decode_int(data, off) + off += INT_OFF + + self.property2_property_id = decode_int(data, off) + off += INT_OFF + + self.property2_value1 = decode_int(data, off) + off += INT_OFF + + self.property2_value2 = decode_int(data, off) + off += INT_OFF + + self.property3_property_id = decode_int(data, off) + off += INT_OFF + + self.property3_value1 = decode_int(data, off) + off += INT_OFF + + self.property3_value2 = decode_int(data, off) + off += INT_OFF + + self.property4_property_id = decode_int(data, off) + off += INT_OFF + + self.property4_value1 = decode_int(data, off) + off += INT_OFF + + self.property4_value2 = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, data: Dict) -> "LinkedSiteRegCampaignRewardData": + ret = cls(b"\x00" * 99, 0) + ret.linked_site_reg_campaign_reward_id = data['LinkedSiteRegCampaignRewardId'] + ret.linked_site_reg_campaign_id = data['LinkedSiteRegCampaignId'] + ret.common_reward_type = data['CommonRewardType'] + ret.common_reward_id = data['CommonRewardId'] + ret.common_reward_num = data['CommonRewardNum'] + ret.strength = data['Strength'] + ret.property1_property_id = data['Property1PropertyId'] + ret.property1_value1 = data['Property1Value1'] + ret.property1_value2 = data['Property1Value2'] + ret.property2_property_id = data['Property2PropertyId'] + ret.property2_value1 = data['Property2Value1'] + ret.property2_value2 = data['Property2Value2'] + ret.property3_property_id = data['Property3PropertyId'] + ret.property3_value1 = data['Property3Value1'] + ret.property3_value2 = data['Property3Value2'] + ret.property4_property_id = data['Property4PropertyId'] + ret.property4_value1 = data['Property4Value1'] + ret.property4_value2 = data['Property4Value2'] + return ret + + def make(self) -> bytes: + return super().make() \ + + encode_int(self.linked_site_reg_campaign_reward_id) \ + + encode_int(self.linked_site_reg_campaign_id) \ + + encode_byte(self.common_reward_type) \ + + encode_int(self.common_reward_id) \ + + encode_short(self.common_reward_num) \ + + encode_int(self.strength) \ + + encode_int(self.property1_property_id) \ + + encode_int(self.property1_value1) \ + + encode_int(self.property1_value2) \ + + encode_int(self.property2_property_id) \ + + encode_int(self.property2_value1) \ + + encode_int(self.property2_value2) \ + + encode_int(self.property3_property_id) \ + + encode_int(self.property3_value1) \ + + encode_int(self.property3_value2) \ + + encode_int(self.property4_property_id) \ + + encode_int(self.property4_value1) \ + + encode_int(self.property4_value2) + +class EpisodeAppendUserData(BaseHelper): + def __init__(self, data: bytes, offset: int): + off = offset + self.user_episode_append_id, new_off = decode_str(data, off) + off += new_off + + self.user_id, new_off = decode_str(data, off) + off += new_off + + self.episode_append_id = decode_int(data, off) + off += INT_OFF + + self.own_num = decode_int(data, off) + off += INT_OFF + + self._sz = off - offset + + @classmethod + def from_args(cls, episode_id: int = 0, user_id: int = 0, episode_append_id: int = 0, own_num: int = 99) -> "EpisodeAppendUserData": + ret = cls(b"\x00" * 996, 0) + ret.user_episode_append_id = episode_id + ret.user_id = user_id + ret.episode_append_id = episode_append_id + ret.own_num = own_num + + def make(self) -> bytes: + return super().make() \ + + encode_str(self.user_episode_append_id) \ + + encode_str(self.user_id) \ + + encode_int(self.episode_append_id) \ + + encode_int(self.own_num) diff --git a/titles/sao/index.py b/titles/sao/index.py index 1f68c43..8a775c6 100644 --- a/titles/sao/index.py +++ b/titles/sao/index.py @@ -1,17 +1,20 @@ from typing import Tuple, Dict, List from starlette.requests import Request -from starlette.responses import Response +from starlette.responses import Response, PlainTextResponse, FileResponse from starlette.routing import Route import yaml import logging, coloredlogs from logging.handlers import TimedRotatingFileHandler from os import path from Crypto.Cipher import Blowfish -from hashlib import md5 +from Crypto.Hash import MD5 import secrets +import traceback +import sys -from core import CoreConfig, Utils +from core import CoreConfig from core.title import BaseServlet +from core.utils import Utils from titles.sao.config import SaoConfig from titles.sao.const import SaoConstants from titles.sao.base import SaoBase @@ -55,13 +58,15 @@ class SaoServlet(BaseServlet): self.static_hash = None if self.game_cfg.hash.verify_hash: - self.static_hash = md5(self.game_cfg.hash.hash_base.encode()).digest() # Greate hashing guys, really validates the data - + self.static_hash = MD5.new(self.game_cfg.hash.hash_base.encode()).digest() # Greate hashing guys, really validates the data + def get_routes(self) -> List[Route]: return [ - Route("/{datecode:int}/proto/if/{category:str}/{endpoint:str}", self.render_POST, methods=['POST']) + Route("/{datecode:int}/proto/if/{category:str}/{endpoint:str}", self.render_POST, methods=['POST']), + Route("/saoresource/{resource_type:str}/{resource_id:int}/{endpoint:str}", self.handle_resource), + Route("/system_status.php", self.render_system_status), ] - + @classmethod def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool: game_cfg = SaoConfig() @@ -75,29 +80,25 @@ class SaoServlet(BaseServlet): return False return True - - def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]: - port_ssl = Utils.get_title_port_ssl(self.core_cfg) - port_normal = Utils.get_title_port(self.core_cfg) - proto = "http" - port = f":{port_normal}" if port_normal != 80 else "" + def get_allnet_info(self, game_id: str, int_ver: int, serial: str) -> Tuple[str, str]: + if self.core_cfg.server.is_using_proxy: + return (f"https://{self.core_cfg.server.hostname}/", "") - if self.game_cfg.server.use_https: - proto = "https" - port = f":{port_ssl}" if port_ssl != 443 else "" + return (f"http://{self.core_cfg.server.hostname}:{Utils.get_title_port(self.core_cfg)}/", "") - return (f"{proto}://{self.core_cfg.server.hostname}{port}/", "") - - - def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, str]: + def get_mucha_info(self, core_cfg: CoreConfig, cfg_dir: str) -> Tuple[bool, List[str], List[str]]: if not self.game_cfg.server.enable: return (False, [], []) return (True, SaoConstants.GAME_CDS, SaoConstants.NETID_PREFIX) + async def render_system_status(self, request: Request) -> bytes: + return PlainTextResponse("open") + async def render_POST(self, request: Request) -> bytes: - endpoint = request.path_params.get('endpoint', '') + endpoint = request.path_params['endpoint'] + ip = Utils.get_ip_addr(request) iv = b"" req_raw = await request.body() @@ -122,10 +123,23 @@ class SaoServlet(BaseServlet): else: req_data = req_raw[40:] - handler = getattr(self.base, f"handle_{cmd_str}", self.base.handle_noop) - self.logger.info(f"{endpoint} - {cmd_str} request") - self.logger.debug(f"Request: {req_raw.hex()}") - resp = await handler(req_header, req_data) + self.logger.debug(f"{endpoint} ({cmd_str}) Request from {ip}: {req_raw.hex()}") + handler = getattr(self.base, f"handle_{cmd_str}", None) + if handler is None: + self.logger.info(f"Using Generic handler for {endpoint}") + handler = self.base.handle_noop + + try: + resp = await handler(req_header, req_data, ip) + + except Exception as e: + self.logger.error(f"Error handling {endpoint} - {e}") + tp, val, tb = sys.exc_info() + traceback.print_exception(tp, val, tb, limit=5) + with open("{0}/{1}.log".format(self.core_cfg.server.log_dir, "sao"), "a") as f: + traceback.print_exception(tp, val, tb, limit=5, file=f) + + resp = SaoNoopResponse(req_header.cmd + 1).make() if resp is None: resp = SaoNoopResponse(req_header.cmd + 1).make() @@ -138,7 +152,7 @@ class SaoServlet(BaseServlet): else: self.logger.error(f"Unknown response type {type(resp)}") - return Response() + return SaoNoopResponse(req_header.cmd + 1).make() self.logger.debug(f"Response: {resp.hex()}") @@ -154,6 +168,17 @@ class SaoServlet(BaseServlet): tmp = struct.pack("!I", crypt_data_len) # does it want the length of the encrypted response?? resp = resp[:20] + tmp + iv + data_crypt self.logger.debug(f"Encrypted Response: {resp.hex()}") - - - return Response(resp, media_type="text/html; charset=utf-8") \ No newline at end of file + + return Response(resp) + + async def handle_resource(self, request: Request) -> bytes: + # TODO: better guard against path traversal attacks + resource_type = request.path_params['resource_type'].replace(".\\", "").replace("..\\", "") + resource_id = request.path_params['resource_id'] + endpoint = request.path_params['endpoint'].replace(".\\", "").replace("..\\", "") + req_ip = Utils.get_ip_addr(request) + + self.logger.debug(f"{req_ip} requested {resource_type} id {resource_id} {endpoint}") + if path.exists(f"./titles/sao/data/{resource_type}/{resource_id}/{endpoint}"): + return FileResponse(f"./titles/sao/data/{resource_type}/{resource_id}/{endpoint}") + return Response(status_code=404) diff --git a/titles/sao/read.py b/titles/sao/read.py index 92aad8c..6299bc4 100644 --- a/titles/sao/read.py +++ b/titles/sao/read.py @@ -1,24 +1,16 @@ -from typing import Optional, Dict, List -from os import walk, path -import urllib import csv +from os import path +from typing import Optional, Dict, List -from read import BaseReader from core.config import CoreConfig -from titles.sao.database import SaoData -from titles.sao.const import SaoConstants +from read import BaseReader +from .database import SaoData +from .const import SaoConstants class SaoReader(BaseReader): - def __init__( - self, - config: CoreConfig, - version: int, - bin_arg: Optional[str], - opt_arg: Optional[str], - extra: Optional[str], - ) -> None: - super().__init__(config, version, bin_arg, opt_arg, extra) + def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None: + super().__init__(config, version, bin_dir, opt_dir, extra) self.data = SaoData(config) try: @@ -28,225 +20,207 @@ class SaoReader(BaseReader): except IndexError: self.logger.error(f"Invalid project SAO version {version}") exit(1) - + async def read(self) -> None: if path.exists(self.bin_dir): await self.read_csv(f"{self.bin_dir}") else: self.logger.warn("Directory not found, nothing to import") - + + def load_csv_file(self, file: str) -> List[Dict]: + ret = [] + try: + fullPath = self.bin_dir + "/" if not self.bin_dir.endswith("/") else "" + fullPath += file + with open(fullPath, encoding="UTF-8") as fp: + reader = csv.DictReader(fp) + for row in reader: + tmp = {} + + fkey = list(row.keys())[0] + new_fkey = fkey.replace("// ", "") + fval = row[fkey] + row.pop(fkey) + row[new_fkey] = fval + + for k,v in row.items(): + if v == "-1": + row[k] = None + elif v.isdigit(): + row[k] = int(v) + elif v.isdecimal(): + row[k] = float(v) + elif v == "True": + row[k] = True + elif v == "False": + row[k] = False + ret.append(row) + + except Exception as e: + self.logger.warning(f"Couldn't read csv file {fullPath}, skipping - {e}") + + return ret async def read_csv(self, bin_dir: str) -> None: self.logger.info(f"Read csv from {bin_dir}") self.logger.info("Now reading QuestScene.csv") - try: - fullPath = bin_dir + "/QuestScene.csv" - with open(fullPath, encoding="UTF-8") as fp: - reader = csv.DictReader(fp) - for row in reader: - questSceneId = row["QuestSceneId"] - sortNo = row["SortNo"] - name = row["Name"] - enabled = True + reader = self.load_csv_file("QuestScene.csv") + if reader: + for row in reader: + self.logger.info(f"Adding quest {row['QuestSceneId']}") + await self.data.static.put_quest(row) - self.logger.info(f"Added quest {questSceneId} | Name: {name}") - - try: - await self.data.static.put_quest( - questSceneId, - 0, - sortNo, - name, - enabled - ) - except Exception as err: - self.logger.error(err) - except Exception: - self.logger.warning(f"Couldn't read csv file in {self.bin_dir}, skipping") - - self.logger.info("Now reading HeroLog.csv") - try: - fullPath = bin_dir + "/HeroLog.csv" - with open(fullPath, encoding="UTF-8") as fp: - reader = csv.DictReader(fp) - for row in reader: - heroLogId = row["HeroLogId"] - name = row["Name"] - nickname = row["Nickname"] - rarity = row["Rarity"] - skillTableSubId = row["SkillTableSubId"] - awakeningExp = row["AwakeningExp"] - flavorText = row["FlavorText"] - enabled = True + self.logger.info("Now reading Property.csv") + reader = self.load_csv_file("Property.csv") + if reader: + for row in reader: + self.logger.info(f"Adding property {row['PropertyId']}") + await self.data.static.put_property(row) - self.logger.info(f"Added hero {heroLogId} | Name: {name}") - - try: - await self.data.static.put_hero( - 0, - heroLogId, - name, - nickname, - rarity, - skillTableSubId, - awakeningExp, - flavorText, - enabled - ) - except Exception as err: - self.logger.error(err) - except Exception: - self.logger.warning(f"Couldn't read csv file in {self.bin_dir}, skipping") - self.logger.info("Now reading Equipment.csv") - try: - fullPath = bin_dir + "/Equipment.csv" - with open(fullPath, encoding="UTF-8") as fp: - reader = csv.DictReader(fp) - for row in reader: - equipmentId = row["EquipmentId"] - equipmentType = row["EquipmentType"] - weaponTypeId = row["WeaponTypeId"] - name = row["Name"] - rarity = row["Rarity"] - flavorText = row["FlavorText"] - enabled = True + reader = self.load_csv_file("Equipment.csv") + if reader: + for row in reader: + self.logger.info(f"Adding equipment {row['EquipmentId']}") + await self.data.static.put_equipment(row) - self.logger.info(f"Added equipment {equipmentId} | Name: {name}") - - try: - await self.data.static.put_equipment( - 0, - equipmentId, - name, - equipmentType, - weaponTypeId, - rarity, - flavorText, - enabled - ) - except Exception as err: - self.logger.error(err) - except Exception: - self.logger.warning(f"Couldn't read csv file in {self.bin_dir}, skipping") + self.logger.info("Now reading Skill.csv") + reader = self.load_csv_file("Skill.csv") + if reader: + for row in reader: + self.logger.info(f"Adding skill {row['SkillId']}") + await self.data.static.put_skill(row) + + self.logger.info("Now reading SkillTable.csv") + reader = self.load_csv_file("SkillTable.csv") + if reader: + for row in reader: + self.logger.info(f"Adding skill table {row['SkillId']} | SubId: {row['SkillTableSubId']}") + await self.data.static.put_skill_table(row['SkillId'], row['SkillTableSubId'], row['Level'], row['AwakeningId'], row['SkillTableId']) + + self.logger.info("Now reading HeroLog.csv") + reader = self.load_csv_file("HeroLog.csv") + if reader: + for row in reader: + self.logger.info(f"Adding hero {row['HeroLogId']}") + await self.data.static.put_hero(row) self.logger.info("Now reading Item.csv") - try: - fullPath = bin_dir + "/Item.csv" - with open(fullPath, encoding="UTF-8") as fp: - reader = csv.DictReader(fp) - for row in reader: - itemId = row["ItemId"] - itemTypeId = row["ItemTypeId"] - name = row["Name"] - rarity = row["Rarity"] - flavorText = row["FlavorText"] - enabled = True + reader = self.load_csv_file("Item.csv") + if reader: + for row in reader: + self.logger.info(f"Adding item {row['ItemId']}") + await self.data.static.put_item(row) - self.logger.info(f"Added item {itemId} | Name: {name}") - - try: - await self.data.static.put_item( - 0, - itemId, - name, - itemTypeId, - rarity, - flavorText, - enabled - ) - except Exception as err: - self.logger.error(err) - except Exception: - self.logger.warning(f"Couldn't read csv file in {self.bin_dir}, skipping") - self.logger.info("Now reading SupportLog.csv") - try: - fullPath = bin_dir + "/SupportLog.csv" - with open(fullPath, encoding="UTF-8") as fp: - reader = csv.DictReader(fp) - for row in reader: - supportLogId = row["SupportLogId"] - charaId = row["CharaId"] - name = row["Name"] - rarity = row["Rarity"] - salePrice = row["SalePrice"] - skillName = row["SkillName"] - enabled = True + reader = self.load_csv_file("SupportLog.csv") + if reader: + for row in reader: + supportLogId = row["SupportLogId"] + charaId = row["CharaId"] + name = row["Name"] + rarity = row["Rarity"] + salePrice = row["SalePrice"] + skillName = row["SkillName"] + enabled = True + + self.logger.info(f"Adding support log {supportLogId}") + await self.data.static.put_support_log( + 0, + supportLogId, + charaId, + name, + rarity, + salePrice, + skillName, + enabled + ) - self.logger.info(f"Added support log {supportLogId} | Name: {name}") - - try: - await self.data.static.put_support_log( - 0, - supportLogId, - charaId, - name, - rarity, - salePrice, - skillName, - enabled - ) - except Exception as err: - self.logger.error(err) - except Exception: - self.logger.warning(f"Couldn't read csv file in {self.bin_dir}, skipping") - self.logger.info("Now reading Title.csv") - try: - fullPath = bin_dir + "/Title.csv" - with open(fullPath, encoding="UTF-8") as fp: - reader = csv.DictReader(fp) - for row in reader: - titleId = row["TitleId"] - displayName = row["DisplayName"] - requirement = row["Requirement"] - rank = row["Rank"] - imageFilePath = row["ImageFilePath"] - enabled = True + reader = self.load_csv_file("Title.csv") + if reader: + for row in reader: + titleId = row["TitleId"] + displayName = row["DisplayName"] + requirement = row["Requirement"] + rank = row["Rank"] + imageFilePath = row["ImageFilePath"] + enabled = True - self.logger.info(f"Added title {titleId} | Name: {displayName}") - - if len(titleId) > 5: - try: - await self.data.static.put_title( - 0, - titleId, - displayName, - requirement, - rank, - imageFilePath, - enabled - ) - except Exception as err: - self.logger.error(err) - elif len(titleId) < 6: # current server code cannot have multiple lengths for the id - continue - except Exception: - self.logger.warning(f"Couldn't read csv file in {self.bin_dir}, skipping") + self.logger.info(f"Adding title {titleId}") + await self.data.static.put_title( + 0, + titleId, + displayName, + requirement, + rank, + imageFilePath, + enabled + ) - self.logger.info("Now reading RareDropTable.csv") - try: - fullPath = bin_dir + "/RareDropTable.csv" - with open(fullPath, encoding="UTF-8") as fp: - reader = csv.DictReader(fp) - for row in reader: - questRareDropId = row["QuestRareDropId"] - commonRewardId = row["CommonRewardId"] - enabled = True + self.logger.info("Now reading QuestRareDrop.csv") + reader = self.load_csv_file("QuestRareDrop.csv") + if reader: + for row in reader: + questRareDropId = row["QuestRareDropId"] + commonRewardId = row["CommonRewardId"] + enabled = True - self.logger.info(f"Added rare drop {questRareDropId} | Reward: {commonRewardId}") - - try: - await self.data.static.put_rare_drop( - 0, - questRareDropId, - commonRewardId, - enabled - ) - except Exception as err: - self.logger.error(err) - except Exception: - self.logger.warning(f"Couldn't read csv file in {self.bin_dir}, skipping") + self.logger.info(f"Adding rare drop {questRareDropId} | Reward: {commonRewardId}") + await self.data.static.put_rare_drop( + 0, + questRareDropId, + commonRewardId, + enabled + ) + + self.logger.info("Now reading RewardTable.csv") + reader = self.load_csv_file("RewardTable.csv") + if reader: + for row in reader: + self.logger.info(f"Adding reward table {row['RewardTableId']} | Sub-ID: {row['RewardTableSubId']} | Reward {row['CommonRewardId']}") + await self.data.static.put_reward_table(row) + + self.logger.info("Now reading ExBonusTable.csv") + reader = self.load_csv_file("ExBonusTable.csv") + if reader: + for row in reader: + self.logger.info(f"Adding ex bonus {row['ExBonusTableId']} | Sub-ID: {row['ExBonusTableSubId']} | Reward {row['CommonRewardId']}") + await self.data.static.put_ex_bonus(row) + + self.logger.info("Now reading PlayerTraceTable.csv") + reader = self.load_csv_file("PlayerTraceTable.csv") + if reader: + for row in reader: + self.logger.info(f"Adding trace table {row['PlayerTraceTableId']} | Sub-ID: {row['PlayerTraceTableSubId']} | Reward {row['CommonRewardId']}") + await self.data.static.put_player_trace(row) + + self.logger.info("Now reading Episode.csv") + reader = self.load_csv_file("Episode.csv") + if reader: + for row in reader: + self.logger.info(f"Adding episode {row['EpisodeId']}") + await self.data.static.put_episode(row) + + self.logger.info("Now reading TrialTower.csv") + reader = self.load_csv_file("TrialTower.csv") + if reader: + for row in reader: + self.logger.info(f"Adding tower {row['TrialTowerId']}") + await self.data.static.put_tower(row) + + self.logger.info("Now reading ExTowerQuests.csv") + reader = self.load_csv_file("ExTowerQuests.csv") + if reader: + for row in reader: + self.logger.info(f"Adding ex tower {row['ExTowerQuestId']}") + await self.data.static.put_ex_tower(row) + + self.logger.info("Now reading SideQuest.csv") + reader = self.load_csv_file("SideQuest.csv") + if reader: + for row in reader: + self.logger.info(f"Adding side quest {row['SideQuestId']}") + await self.data.static.put_side_quest(row) diff --git a/titles/sao/schema/item.py b/titles/sao/schema/item.py index 332e135..abadca5 100644 --- a/titles/sao/schema/item.py +++ b/titles/sao/schema/item.py @@ -1,6 +1,6 @@ from typing import Optional, Dict, List from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, case -from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON +from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BOOLEAN, INTEGER, BIGINT from sqlalchemy.schema import ForeignKey from sqlalchemy.sql import func, select, update, delete from sqlalchemy.engine import Row @@ -17,15 +17,30 @@ equipment_data = Table( ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False, ), - Column("equipment_id", Integer, nullable=False), + Column("equipment_id", BIGINT, ForeignKey("sao_static_equipment_list.EquipmentId", ondelete="cascade", onupdate="cascade"), nullable=False), Column("enhancement_value", Integer, nullable=False), Column("enhancement_exp", Integer, nullable=False), Column("awakening_exp", Integer, nullable=False), Column("awakening_stage", Integer, nullable=False), Column("possible_awakening_flag", Integer, nullable=False), + Column("is_shop_purchase", BOOLEAN, nullable=False, server_default="0"), + Column("is_protect", BOOLEAN, nullable=False, server_default="0"), + Column("property1_property_id", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False, server_default="2"), + Column("property1_value1", INTEGER, nullable=False, server_default="0"), + Column("property1_value2", INTEGER, nullable=False, server_default="0"), + Column("property2_property_id", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False, server_default="2"), + Column("property2_value1", INTEGER, nullable=False, server_default="0"), + Column("property2_value2", INTEGER, nullable=False, server_default="0"), + Column("property3_property_id", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False, server_default="2"), + Column("property3_value1", INTEGER, nullable=False, server_default="0"), + Column("property3_value2", INTEGER, nullable=False, server_default="0"), + Column("property4_property_id", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False, server_default="2"), + Column("property4_value1", INTEGER, nullable=False, server_default="0"), + Column("property4_value2", INTEGER, nullable=False, server_default="0"), + Column("converted_card_num", INTEGER, nullable=False, server_default="0"), Column("get_date", TIMESTAMP, nullable=False, server_default=func.now()), UniqueConstraint("user", "equipment_id", name="sao_equipment_data_uk"), - mysql_charset="utf8mb4", + mysql_charset="utf8mb4" ) item_data = Table( @@ -40,7 +55,7 @@ item_data = Table( Column("item_id", Integer, nullable=False), Column("get_date", TIMESTAMP, nullable=False, server_default=func.now()), UniqueConstraint("user", "item_id", name="sao_item_data_uk"), - mysql_charset="utf8mb4", + mysql_charset="utf8mb4" ) hero_log_data = Table( @@ -52,19 +67,38 @@ hero_log_data = Table( ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False, ), - Column("user_hero_log_id", Integer, nullable=False), + Column("hero_log_id", BIGINT, ForeignKey("sao_static_hero_list.HeroLogId", ondelete="cascade", onupdate="cascade"), nullable=False), Column("log_level", Integer, nullable=False), Column("log_exp", Integer, nullable=False), - Column("main_weapon", Integer, nullable=False), - Column("sub_equipment", Integer, nullable=False), - Column("skill_slot1_skill_id", Integer, nullable=False), - Column("skill_slot2_skill_id", Integer, nullable=False), - Column("skill_slot3_skill_id", Integer, nullable=False), - Column("skill_slot4_skill_id", Integer, nullable=False), - Column("skill_slot5_skill_id", Integer, nullable=False), + Column("main_weapon", BIGINT, ForeignKey("sao_equipment_data.id", ondelete="set null", onupdate="set null")), + Column("sub_equipment", BIGINT, ForeignKey("sao_equipment_data.id", ondelete="set null", onupdate="set null")), + Column("skill_slot1_skill_id", BIGINT, ForeignKey("sao_static_skill.SkillId", ondelete="set null", onupdate="set null")), + Column("skill_slot2_skill_id", BIGINT, ForeignKey("sao_static_skill.SkillId", ondelete="set null", onupdate="set null")), + Column("skill_slot3_skill_id", BIGINT, ForeignKey("sao_static_skill.SkillId", ondelete="set null", onupdate="set null")), + Column("skill_slot4_skill_id", BIGINT, ForeignKey("sao_static_skill.SkillId", ondelete="set null", onupdate="set null")), + Column("skill_slot5_skill_id", BIGINT, ForeignKey("sao_static_skill.SkillId", ondelete="set null", onupdate="set null")), + Column("max_level_extend_num", INTEGER, nullable=False, server_default="0"), + Column("is_awakenable", BOOLEAN, nullable=False, server_default="0"), + Column("awakening_stage", INTEGER, nullable=False, server_default="0"), + Column("awakening_exp", INTEGER, nullable=False, server_default="0"), + Column("is_shop_purchase", BOOLEAN, nullable=False, server_default="0"), + Column("is_protect", BOOLEAN, nullable=False, server_default="0"), + Column("property1_property_id", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False, server_default="2"), + Column("property1_value1", INTEGER, nullable=False, server_default="0"), + Column("property1_value2", INTEGER, nullable=False, server_default="0"), + Column("property2_property_id", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False, server_default="2"), + Column("property2_value1", INTEGER, nullable=False, server_default="0"), + Column("property2_value2", INTEGER, nullable=False, server_default="0"), + Column("property3_property_id", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False, server_default="2"), + Column("property3_value1", INTEGER, nullable=False, server_default="0"), + Column("property3_value2", INTEGER, nullable=False, server_default="0"), + Column("property4_property_id", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False, server_default="2"), + Column("property4_value1", INTEGER, nullable=False, server_default="0"), + Column("property4_value2", INTEGER, nullable=False, server_default="0"), + Column("converted_card_num", INTEGER, nullable=False, server_default="0"), Column("get_date", TIMESTAMP, nullable=False, server_default=func.now()), - UniqueConstraint("user", "user_hero_log_id", name="sao_hero_log_data_uk"), - mysql_charset="utf8mb4", + UniqueConstraint("user", "hero_log_id", name="sao_hero_log_data_uk"), + mysql_charset="utf8mb4" ) hero_party = Table( @@ -77,11 +111,11 @@ hero_party = Table( nullable=False, ), Column("user_party_team_id", Integer, nullable=False), - Column("user_hero_log_id_1", Integer, nullable=False), - Column("user_hero_log_id_2", Integer, nullable=False), - Column("user_hero_log_id_3", Integer, nullable=False), + Column("user_hero_log_id_1", Integer, ForeignKey("sao_hero_log_data.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("user_hero_log_id_2", Integer, ForeignKey("sao_hero_log_data.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("user_hero_log_id_3", Integer, ForeignKey("sao_hero_log_data.id", ondelete="cascade", onupdate="cascade"), nullable=False), UniqueConstraint("user", "user_party_team_id", name="sao_hero_party_uk"), - mysql_charset="utf8mb4", + mysql_charset="utf8mb4" ) quest = Table( @@ -93,15 +127,28 @@ quest = Table( ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False, ), - Column("episode_id", Integer, nullable=False), + Column("quest_type", INTEGER, nullable=False, server_default="1"), + Column("quest_scene_id", BIGINT, ForeignKey("sao_static_quest.QuestSceneId", ondelete="cascade", onupdate="cascade"), nullable=False), Column("quest_clear_flag", Boolean, nullable=False), Column("clear_time", Integer, nullable=False), Column("combo_num", Integer, nullable=False), Column("total_damage", Integer, nullable=False), Column("concurrent_destroying_num", Integer, nullable=False), Column("play_date", TIMESTAMP, nullable=False, server_default=func.now()), - UniqueConstraint("user", "episode_id", name="sao_player_quest_uk"), - mysql_charset="utf8mb4", + UniqueConstraint("user", "quest_scene_id", name="sao_player_quest_uk"), + mysql_charset="utf8mb4" +) + +ex_bonus = Table( + "sao_player_ex_bonus", + metadata, + Column("id", BIGINT, primary_key=True, nullable=False), + Column("user", INTEGER, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("quest_scene_id", BIGINT, ForeignKey("sao_static_quest.QuestSceneId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("ex_bonus_table_id", BIGINT, ForeignKey("sao_static_ex_bonus.ExBonusTableId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("quest_clear_flag", BOOLEAN, nullable=False, server_default="0"), + UniqueConstraint("user", "quest_scene_id", "ex_bonus_table_id", name="sao_player_ex_bonus_uk"), + mysql_charset="utf8mb4" ) sessions = Table( @@ -114,12 +161,12 @@ sessions = Table( nullable=False, ), Column("user_party_team_id", Integer, nullable=False), - Column("episode_id", Integer, nullable=False), + Column("episode_id", Integer, nullable=False), # TODO: Change to quest scene id Column("play_mode", Integer, nullable=False), Column("quest_drop_boost_apply_flag", Integer, nullable=False), Column("play_date", TIMESTAMP, nullable=False, server_default=func.now()), UniqueConstraint("user", "user_party_team_id", "play_date", name="sao_play_sessions_uk"), - mysql_charset="utf8mb4", + mysql_charset="utf8mb4" ) end_sessions = Table( @@ -135,7 +182,7 @@ end_sessions = Table( Column("play_result_flag", Boolean, nullable=False), Column("reward_data", JSON, nullable=True), Column("play_date", TIMESTAMP, nullable=False, server_default=func.now()), - mysql_charset="utf8mb4", + mysql_charset="utf8mb4" ) class SaoItemData(BaseData): @@ -148,13 +195,17 @@ class SaoItemData(BaseData): quest_drop_boost_apply_flag=quest_drop_boost_apply_flag ) - conflict = sql.on_duplicate_key_update(user=user_id) + conflict = sql.on_duplicate_key_update( + user_party_team_id=user_party_team_id, + episode_id=episode_id, + play_mode=play_mode, + quest_drop_boost_apply_flag=quest_drop_boost_apply_flag + ) result = await self.execute(conflict) - if result is None: - self.logger.error(f"Failed to create SAO session for user {user_id}!") - return None - return result.lastrowid + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to create SAO session for user {user_id}!") async def create_end_session(self, user_id: int, quest_id: int, play_result_flag: bool, reward_data: JSON) -> Optional[int]: sql = insert(end_sessions).values( @@ -164,13 +215,16 @@ class SaoItemData(BaseData): reward_data=reward_data, ) - conflict = sql.on_duplicate_key_update(user=user_id) + conflict = sql.on_duplicate_key_update( + play_result_flag=play_result_flag, + reward_data=reward_data + ) result = await self.execute(conflict) - if result is None: - self.logger.error(f"Failed to create SAO end session for user {user_id}!") - return None - return result.lastrowid + if result: + return result.inserted_primary_key['id'] + + self.logger.error(f"Failed to create SAO end session for user {user_id}!") async def put_item(self, user_id: int, item_id: int) -> Optional[int]: sql = insert(item_data).values( @@ -178,51 +232,93 @@ class SaoItemData(BaseData): item_id=item_id, ) - conflict = sql.on_duplicate_key_update( - item_id=item_id, - ) + conflict = sql.on_duplicate_key_update(item_id=item_id) result = await self.execute(conflict) - if result is None: - self.logger.error( - f"{__name__} failed to insert item! user: {user_id}, item_id: {item_id}" - ) - return None - - return result.lastrowid + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert item! user: {user_id}, item_id: {item_id}") - async def put_equipment_data(self, user_id: int, equipment_id: int, enhancement_value: int, enhancement_exp: int, awakening_exp: int, awakening_stage: int, possible_awakening_flag: int) -> Optional[int]: + async def put_equipment(self, user_id: int, equipment_id: int) -> Optional[int]: sql = insert(equipment_data).values( user=user_id, equipment_id=equipment_id, - enhancement_value=enhancement_value, - enhancement_exp=enhancement_exp, - awakening_exp=awakening_exp, - awakening_stage=awakening_stage, - possible_awakening_flag=possible_awakening_flag, + enhancement_value=1, + enhancement_exp=200, + awakening_exp=0, + awakening_stage=0, + possible_awakening_flag=0, ) - conflict = sql.on_duplicate_key_update( - enhancement_value=enhancement_value, - enhancement_exp=enhancement_exp, - awakening_exp=awakening_exp, - awakening_stage=awakening_stage, - possible_awakening_flag=possible_awakening_flag, - ) + conflict = sql.on_duplicate_key_update(equipment_id=equipment_id) result = await self.execute(conflict) - if result is None: - self.logger.error( - f"{__name__} failed to insert equipment! user: {user_id}, equipment_id: {equipment_id}" - ) - return None + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert equipment! user: {user_id}, equipment_id: {equipment_id}") + + async def put_ex_bonus(self, user_id: int, quest_scene_id: int, ex_bonus_id: int, clear: bool = False) -> Optional[int]: + sql = insert(ex_bonus).values( + user=user_id, + quest_scene_id=quest_scene_id, + ex_bonus_table_id=ex_bonus_id, + quest_clear_flag=clear, + ) - return result.lastrowid + conflict = sql.on_duplicate_key_update(quest_clear_flag=clear) + result = await self.execute(conflict) + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert ex bonus status! user: {user_id}, quest_scene_id: {quest_scene_id}, ex_bonus_id: {ex_bonus_id}, clear: {clear}") + + async def add_equipment_enhancement_exp(self, user_weapon_id: int, enhancement_exp: int) -> None: + result = await self.execute( + equipment_data.update(equipment_data.c.id == user_weapon_id) + .values(enhancement_exp=equipment_data.c.enhancement_exp + enhancement_exp) + ) + if not result: + self.logger.error(f"Failed to give equipment {user_weapon_id} {enhancement_exp} xp!") + + async def get_equipment_enhancement_exp(self, user_weapon_id: int) -> Optional[int]: + result = await self.execute(select(equipment_data.c.enhancement_exp).where(equipment_data.c.id==user_weapon_id)) + if result: + row = result.fetchone() + if row: + return row['enhancement_exp'] + return 0 + self.logger.error(f"Failed to get equipment {user_weapon_id} xp!") + + async def set_equipment_enhancement_value(self, user_equip_id: int, enhancement_val: int) -> None: + result = await self.execute(equipment_data.update(equipment_data.c.id == user_equip_id).values(enhancement_value=enhancement_val)) + if result is None: + self.logger.error(f"Failed to set equipment {user_equip_id} level to {enhancement_val}!") + + async def add_hero_xp(self, user_hero_log_id: int, add_xp: int) -> Optional[int]: + result = await self.execute( + hero_log_data.update(hero_log_data.c.id == user_hero_log_id) + .values(log_exp=hero_log_data.c.log_exp + add_xp) + ) + if not result: + self.logger.error(f"Failed to give hero {user_hero_log_id} {add_xp} xp!") + + async def get_hero_xp(self, user_hero_log_id: int) -> Optional[int]: + result = await self.execute(select(hero_log_data.c.log_exp).where(hero_log_data.c.id==user_hero_log_id)) + if result: + row = result.fetchone() + if row: + return row['log_exp'] + return 0 + self.logger.error(f"Failed to get hero xp for {user_hero_log_id}") + + async def set_hero_level(self, user_hero_log_id: int, new_level: int): + result = await self.execute(hero_log_data.update(hero_log_data.c.id == user_hero_log_id).values(log_level=new_level)) + if result is None: + self.logger.error(f"Failed to set hero {user_hero_log_id} level to {new_level}!") async def put_hero_log(self, user_id: int, user_hero_log_id: int, log_level: int, log_exp: int, main_weapon: int, sub_equipment: int, skill_slot1_skill_id: int, skill_slot2_skill_id: int, skill_slot3_skill_id: int, skill_slot4_skill_id: int, skill_slot5_skill_id: int) -> Optional[int]: sql = insert(hero_log_data).values( user=user_id, - user_hero_log_id=user_hero_log_id, + hero_log_id=user_hero_log_id, log_level=log_level, log_exp=log_exp, main_weapon=main_weapon, @@ -243,17 +339,36 @@ class SaoItemData(BaseData): skill_slot2_skill_id=skill_slot2_skill_id, skill_slot3_skill_id=skill_slot3_skill_id, skill_slot4_skill_id=skill_slot4_skill_id, - skill_slot5_skill_id=skill_slot5_skill_id, + skill_slot5_skill_id=skill_slot5_skill_id ) result = await self.execute(conflict) + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert hero! user: {user_id}, user_hero_log_id: {user_hero_log_id}") + + async def set_user_hero_weapons(self, user_hero_id: int, main_weapon: int, sub_weapon: int) -> None: + sql = hero_log_data.update(hero_log_data.c.id == user_hero_id).values( + main_weapon=main_weapon, + sub_equipment=sub_weapon, + ) + result = await self.execute(sql) if result is None: - self.logger.error( - f"{__name__} failed to insert hero! user: {user_id}, user_hero_log_id: {user_hero_log_id}" - ) + self.logger.error(f"Failed to update user hero {user_hero_id} weapons {main_weapon}/{sub_weapon}") return None - return result.lastrowid + async def set_user_hero_skills(self, user_hero_id: int, skill1: int, skill2: int, skill3: int, skill4: int, skill5: int) -> None: + sql = hero_log_data.update(hero_log_data.c.id == user_hero_id).values( + skill_slot1_skill_id = skill1, + skill_slot2_skill_id = skill2, + skill_slot3_skill_id = skill3, + skill_slot4_skill_id = skill4, + skill_slot5_skill_id = skill5, + ) + result = await self.execute(sql) + if result is None: + self.logger.error(f"Failed to update user hero {user_hero_id} skills {skill1}/{skill2}/{skill3}/{skill4}/{skill5}") + return None async def put_hero_party(self, user_id: int, user_party_team_id: int, user_hero_log_id_1: int, user_hero_log_id_2: int, user_hero_log_id_3: int) -> Optional[int]: sql = insert(hero_party).values( @@ -267,22 +382,19 @@ class SaoItemData(BaseData): conflict = sql.on_duplicate_key_update( user_hero_log_id_1=user_hero_log_id_1, user_hero_log_id_2=user_hero_log_id_2, - user_hero_log_id_3=user_hero_log_id_3, + user_hero_log_id_3=user_hero_log_id_3 ) result = await self.execute(conflict) - if result is None: - self.logger.error( - f"{__name__} failed to insert hero party! user: {user_id}, user_party_team_id: {user_party_team_id}" - ) - return None + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert hero party! user: {user_id}, user_party_team_id: {user_party_team_id}") - return result.lastrowid - - async def put_player_quest(self, user_id: int, episode_id: int, quest_clear_flag: bool, clear_time: int, combo_num: int, total_damage: int, concurrent_destroying_num: int) -> Optional[int]: + async def put_player_quest(self, user_id: int, quest_type: int, quest_scene_id: int, quest_clear_flag: bool, clear_time: int, combo_num: int, total_damage: int, concurrent_destroying_num: int) -> Optional[int]: sql = insert(quest).values( user=user_id, - episode_id=episode_id, + quest_type=quest_type, + quest_scene_id=quest_scene_id, quest_clear_flag=quest_clear_flag, clear_time=clear_time, combo_num=combo_num, @@ -299,13 +411,9 @@ class SaoItemData(BaseData): ) result = await self.execute(conflict) - if result is None: - self.logger.error( - f"{__name__} failed to insert quest! user: {user_id}, episode_id: {episode_id}" - ) - return None - - return result.lastrowid + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert quest! user: {user_id}, quest_scene_id: {quest_scene_id}") async def get_user_equipment(self, user_id: int, equipment_id: int) -> Optional[Dict]: sql = equipment_data.select(equipment_data.c.user == user_id and equipment_data.c.equipment_id == equipment_id) @@ -315,6 +423,14 @@ class SaoItemData(BaseData): return None return result.fetchone() + async def get_user_equipment_by_id(self, equipment_user_id: int) -> Optional[Row]: + sql = equipment_data.select(equipment_data.c.id == equipment_user_id) + + result = await self.execute(sql) + if result is None: + return None + return result.fetchone() + async def get_user_equipments( self, user_id: int ) -> Optional[List[Row]]: @@ -349,6 +465,38 @@ class SaoItemData(BaseData): return None return result.fetchall() + async def get_player_ex_bonus_status(self, user_id: int) -> Optional[List[Row]]: + sql = equipment_data.select(ex_bonus.c.user == user_id) + + result = await self.execute(sql) + if result is None: + return None + return result.fetchall() + + async def get_player_ex_bonus_by_quest(self, user_id: int, quest_scene_id) -> Optional[List[Row]]: + sql = ex_bonus.select(and_(ex_bonus.c.user == user_id, ex_bonus.c.quest_scene_id == quest_scene_id)) + + result = await self.execute(sql) + if result is None: + return None + return result.fetchall() + + async def get_user_item_by_id( + self, user_item_id: int + ) -> Optional[Row]: + sql = item_data.select(item_data.c.id == user_item_id) + + result = await self.execute(sql) + if result is None: + return None + return result.fetchone() + + async def get_user_hero_by_id(self, user_hero_id: int) -> Optional[Row]: + result = await self.execute(hero_log_data.select(hero_log_data.c.id == user_hero_id)) + if result is None: + return None + return result.fetchone() + async def get_hero_log( self, user_id: int, user_hero_log_id: int = None ) -> Optional[List[Row]]: @@ -358,7 +506,7 @@ class SaoItemData(BaseData): sql = hero_log_data.select( and_( hero_log_data.c.user == user_id, - hero_log_data.c.user_hero_log_id == user_hero_log_id if user_hero_log_id is not None else True, + hero_log_data.c.hero_log_id == user_hero_log_id if user_hero_log_id is not None else True, ) ) @@ -366,6 +514,12 @@ class SaoItemData(BaseData): if result is None: return None return result.fetchone() + + async def get_hero_log_by_id(self, user_hero_log_id: int) -> Optional[Row]: + result = await self.execute(hero_log_data.select(hero_log_data.c.id == user_hero_log_id)) + if result is None: + return None + return result.fetchone() async def get_hero_logs( self, user_id: int @@ -373,17 +527,17 @@ class SaoItemData(BaseData): """ A catch-all hero lookup given a profile """ - sql = hero_log_data.select( - and_( - hero_log_data.c.user == user_id, - ) - ) - - result = await self.execute(sql) + result = await self.execute(hero_log_data.select(hero_log_data.c.user == user_id)) if result is None: return None return result.fetchall() + async def get_hero_party_by_id(self, party_id: int) -> Optional[Row]: + result = await self.execute(hero_party.select(hero_party.c.id == party_id)) + if result is None: + return None + return result.fetchone() + async def get_hero_party( self, user_id: int, user_party_team_id: int = None ) -> Optional[List[Row]]: @@ -397,18 +551,18 @@ class SaoItemData(BaseData): result = await self.execute(sql) if result is None: return None - return result.fetchone() + return result.fetchall() async def get_quest_log( - self, user_id: int, episode_id: int = None - ) -> Optional[List[Row]]: + self, user_id: int, quest_scene_id: int + ) -> Optional[Row]: """ - A catch-all quest lookup given a profile and episode_id + A catch-all quest lookup given a profile and quest_scene_id """ sql = quest.select( and_( quest.c.user == user_id, - quest.c.episode_id == episode_id if episode_id is not None else True, + quest.c.quest_scene_id == quest_scene_id ) ) @@ -420,16 +574,7 @@ class SaoItemData(BaseData): async def get_quest_logs( self, user_id: int ) -> Optional[List[Row]]: - """ - A catch-all quest lookup given a profile - """ - sql = quest.select( - and_( - quest.c.user == user_id, - ) - ) - - result = await self.execute(sql) + result = await self.execute(quest.select(quest.c.user == user_id)) if result is None: return None return result.fetchall() @@ -437,13 +582,7 @@ class SaoItemData(BaseData): async def get_session( self, user_id: int = None ) -> Optional[List[Row]]: - sql = sessions.select( - and_( - sessions.c.user == user_id, - ) - ).order_by( - sessions.c.play_date.asc() - ) + sql = sessions.select(sessions.c.user == user_id).order_by(sessions.c.play_date.desc()) result = await self.execute(sql) if result is None: @@ -453,54 +592,36 @@ class SaoItemData(BaseData): async def get_end_session( self, user_id: int = None ) -> Optional[List[Row]]: - sql = end_sessions.select( - and_( - end_sessions.c.user == user_id, - ) - ).order_by( - end_sessions.c.play_date.desc() - ) + sql = end_sessions.select(end_sessions.c.user == user_id).order_by(end_sessions.c.play_date.desc()) result = await self.execute(sql) if result is None: return None return result.fetchone() - - async def remove_hero_log(self, user_id: int, user_hero_log_id: int) -> None: - sql = hero_log_data.delete( - and_( - hero_log_data.c.user == user_id, - hero_log_data.c.user_hero_log_id == user_hero_log_id, - ) - ) + + async def remove_end_session(self, end_id: int) -> None: + result = await self.execute(end_sessions.delete(end_sessions.c.id == end_id)) + if result is None: + self.logger.error(f"Failed to delete end session {end_id}") + + async def remove_hero_log(self, user_hero_log_id: int) -> None: + sql = hero_log_data.delete(hero_log_data.c.id == user_hero_log_id) result = await self.execute(sql) - if result is None: - self.logger.error( - f"{__name__} failed to remove hero log! profile: {user_id}, user_hero_log_id: {user_hero_log_id}" - ) - return None + if not result: + self.logger.error(f"Failed to remove hero log id: {user_hero_log_id}") - async def remove_equipment(self, user_id: int, equipment_id: int) -> None: - sql = equipment_data.delete( - and_(equipment_data.c.user == user_id, equipment_data.c.equipment_id == equipment_id) - ) + async def remove_equipment(self, equipment_id: int) -> None: + sql = equipment_data.delete(equipment_data.c.id == equipment_id) result = await self.execute(sql) - if result is None: - self.logger.error( - f"{__name__} failed to remove equipment! profile: {user_id}, equipment_id: {equipment_id}" - ) - return None + if not result: + self.logger.error(f"Failed to remove equipment id {equipment_id}") - async def remove_item(self, user_id: int, item_id: int) -> None: - sql = item_data.delete( - and_(item_data.c.user == user_id, item_data.c.item_id == item_id) - ) + async def remove_item(self, user_item_id: int) -> None: + sql = item_data.delete(item_data.c.id == user_item_id) result = await self.execute(sql) - if result is None: - self.logger.error( - f"{__name__} failed to remove item! profile: {user_id}, item_id: {item_id}" - ) - return None \ No newline at end of file + if not result: + self.logger.error(f"Failed to remove item {user_item_id}!") + diff --git a/titles/sao/schema/profile.py b/titles/sao/schema/profile.py index 27d284d..4a6df93 100644 --- a/titles/sao/schema/profile.py +++ b/titles/sao/schema/profile.py @@ -1,13 +1,12 @@ -from typing import Optional, Dict, List +from typing import Optional, Tuple, List from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, case -from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON +from sqlalchemy.types import Integer, String, BOOLEAN, INTEGER, BIGINT, VARCHAR, TIMESTAMP from sqlalchemy.schema import ForeignKey -from sqlalchemy.sql import func, select, update, delete +from sqlalchemy.sql import func, select from sqlalchemy.engine import Row from sqlalchemy.dialects.mysql import insert from core.data.schema import BaseData, metadata -from ..const import SaoConstants profile = Table( "sao_profile", @@ -24,9 +23,70 @@ profile = Table( Column("rank_num", Integer, server_default="1"), Column("rank_exp", Integer, server_default="0"), Column("own_col", Integer, server_default="0"), - Column("own_vp", Integer, server_default="300"), + Column("own_vp", Integer, server_default="0"), Column("own_yui_medal", Integer, server_default="0"), Column("setting_title_id", Integer, server_default="20005"), + Column("my_shop", INTEGER), + Column("fav_hero", INTEGER, ForeignKey("sao_hero_log_data.id", ondelete="set null", onupdate="cascade")), + Column("when_register", TIMESTAMP, server_default=func.now()), + Column("last_login_date", TIMESTAMP), + Column("last_yui_medal_date", TIMESTAMP), + Column("last_bonus_yui_medal_date", TIMESTAMP), + Column("last_comeback_date", TIMESTAMP), + Column("last_login_bonus_date", TIMESTAMP), + Column("ad_confirm_date", TIMESTAMP), + Column("login_ct", INTEGER, server_default="0"), + mysql_charset="utf8mb4" +) + +beginner_mission = Table( + "sao_player_beginner_mission", + metadata, + Column("id", BIGINT, primary_key=True, nullable=False), + Column("user", INTEGER, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False, unique=True), + Column("beginner_mission_id", INTEGER, nullable=False), + Column("condition_id", INTEGER, nullable=False), + Column("is_seat", BOOLEAN, nullable=False, server_default="0"), + Column("achievement_num", INTEGER, nullable=False), + Column("complete_flag", BOOLEAN, nullable=False, server_default="0"), + Column("complete_date", TIMESTAMP), + Column("reward_received_flag", BOOLEAN, nullable=False, server_default="0"), + Column("reward_received_date", TIMESTAMP), + UniqueConstraint("user", "condition_id", name="sao_player_beginner_mission_uk"), + mysql_charset="utf8mb4" +) + +resource_card = Table( + "sao_player_resource_card", + metadata, + Column("id", BIGINT, primary_key=True, nullable=False), + Column("user", INTEGER, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("common_reward_type", INTEGER, nullable=False), + Column("common_reward_id", INTEGER, nullable=False), + Column("holographic_flag", BOOLEAN, nullable=False, server_default="0"), + Column("serial", VARCHAR(20), unique=True), + mysql_charset="utf8mb4" +) + +hero_card = Table( + "sao_player_hero_card", + metadata, + Column("id", BIGINT, primary_key=True, nullable=False), + Column("user", INTEGER, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("user_hero_id", INTEGER, ForeignKey("sao_hero_log_data.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("holographic_flag", BOOLEAN, nullable=False, server_default="0"), + Column("serial", VARCHAR(20), unique=True), + mysql_charset="utf8mb4" +) + +tutorial = Table( + "sao_player_tutorial", + metadata, + Column("id", BIGINT, primary_key=True, nullable=False), + Column("user", INTEGER, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("tutorial_byte", INTEGER, nullable=False), + UniqueConstraint("user", "tutorial_byte", name="sao_player_tutorial_uk"), + mysql_charset="utf8mb4" ) class SaoProfileData(BaseData): @@ -35,10 +95,81 @@ class SaoProfileData(BaseData): conflict = sql.on_duplicate_key_update(user=user_id) result = await self.execute(conflict) + if result: + return result.inserted_primary_key['id'] + self.logger.error("Failed to create SAO profile!") + + async def set_my_shop(self, user_id: int, store_id: int): + result = await self.execute(profile.update(profile.c.user == user_id).values(my_shop = store_id)) if result is None: - self.logger.error(f"Failed to create SAO profile for user {user_id}!") - return None - return result.lastrowid + self.logger.error(f"Failed to set my shop for user {user_id} to {store_id}!") + + async def user_login(self, user_id: int) -> Optional[Row]: + sql = profile.update(profile.c.user == user_id).values( + login_ct=profile.c.login_ct + 1, + last_login_date=func.now() + ) + result = await self.execute(sql) + if result: + return result.last_updated_params() + self.logger.error(f"Failed to create log in user {user_id}!") + + async def update_yui_medal_date(self, user_id: int) -> None: + sql = profile.update(profile.c.user == user_id).values( + last_yui_medal_date=func.now() + ) + result = await self.execute(sql) + if result is None: + self.logger.error(f"Failed to update user {user_id} yui medal date!") + + async def add_yui_medals(self, user_id: int, num_medals: int = 1): + sql = profile.update(profile.c.user == user_id).values( + own_yui_medal=profile.c.own_yui_medal + num_medals + ) + result = await self.execute(sql) + if result is None: + self.logger.error(f"Failed to give user {user_id} {num_medals} yui medals!") + + async def add_col(self, user_id: int, num_col: int) -> None: + sql = profile.update(profile.c.user == user_id).values( + own_col=profile.c.own_col + num_col + ) + result = await self.execute(sql) + if not result: + self.logger.error(f"Failed to give user {user_id} {num_col} Col!") + + async def add_vp(self, user_id: int, num_vp: int) -> None: + sql = profile.update(profile.c.user == user_id).values( + own_vp=profile.c.own_vp + num_vp + ) + result = await self.execute(sql) + if not result: + self.logger.error(f"Failed to give user {user_id} {num_vp} VP!") + + async def add_exp(self, user_id: int, xp_ammount: int) -> Optional[int]: + sql = profile.update(profile.c.user == user_id).values( + rank_exp=profile.c.rank_exp + xp_ammount + ) + result = await self.execute(sql) + if not result: + self.logger.error(f"Failed to give user {user_id} {xp_ammount} xp!") + + async def get_exp(self, user_id: int) -> Optional[int]: + result = await self.execute(select(profile.c.rank_exp).where(profile.c.user==user_id)) + if result: + row = result.fetchone() + if row: + return row['rank_exp'] + return 0 + self.logger.error(f"Failed to query rank xp for user {user_id}") + + async def set_level(self, user_id: int, level: int): + sql = profile.update(profile.c.user == user_id).values( + rank_num=level + ) + result = await self.execute(sql) + if result is None: + self.logger.error(f"Failed to set user {user_id} level to {level}!") async def put_profile(self, user_id: int, user_type: int, nick_name: str, rank_num: int, rank_exp: int, own_col: int, own_vp: int, own_yui_medal: int, setting_title_id: int) -> Optional[int]: sql = insert(profile).values( @@ -63,17 +194,118 @@ class SaoProfileData(BaseData): ) result = await self.execute(conflict) - if result is None: - self.logger.error( - f"{__name__} failed to insert profile! user: {user_id}" - ) - return None - - return result.lastrowid + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert profile! user: {user_id}") async def get_profile(self, user_id: int) -> Optional[Row]: sql = profile.select(profile.c.user == user_id) result = await self.execute(sql) if result is None: return None - return result.fetchone() \ No newline at end of file + return result.fetchone() + + async def set_profile_name(self, user_id: int, new_name: str) -> None: + sql = profile.update(profile.c.user == user_id).values( + nick_name=new_name + ) + result = await self.execute(sql) + if result is None: + self.logger.error(f"Failed to update nickname {new_name} for user {user_id}") + + async def add_tutorial_byte(self, user_id: int, tutorial_byte: int) -> None: + sql = insert(tutorial).values( + user = user_id, + tutorial_byte = tutorial_byte + ) + + conflict = sql.on_duplicate_key_update(tutorial_byte = tutorial_byte) + result = await self.execute(conflict) + if result is None: + self.logger.error(f"Failed to add tutorial byte {tutorial_byte} to user {user_id}") + + async def get_tutorial_bytes(self, user_id: int) -> Optional[List[Row]]: + sql = tutorial.select(tutorial.c.user == user_id) + + result = await self.execute(sql) + if result is None: + return None + return result.fetchall() + + async def put_hero_card(self, user_id: int, serial: str, user_hero_id: int, is_holo: bool) -> Optional[int]: + sql = insert(hero_card).values( + user=user_id, + user_hero_id=user_hero_id, + holographic_flag=is_holo, + serial=serial + ) + + conflict = sql.on_duplicate_key_update( + holographic_flag=is_holo, + serial=serial + ) + result = await self.execute(conflict) + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert card {serial} for user {user_id} as hero {user_hero_id}") + + async def get_hero_card(self, serial: str) -> Optional[Row]: + result = await self.execute(hero_card.select(hero_card.c.serial == serial)) + if result is None: + return None + return result.fetchone() + + async def put_resource_card(self, user_id: int, serial: str, reward_type: int, reward_id: int, is_holo: bool) -> Optional[int]: + sql = insert(resource_card).values( + user=user_id, + common_reward_type=reward_type, + common_reward_id=reward_id, + holographic_flag=is_holo, + serial=serial + ) + + conflict = sql.on_duplicate_key_update( + holographic_flag=is_holo, + serial=serial + ) + result = await self.execute(conflict) + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert card {serial} for user {user_id} as resource {reward_id}") + + async def get_resource_card(self, serial: str) -> Optional[int]: + result = await self.execute(resource_card.select(resource_card.c.serial == serial)) + if result is None: + return None + return result.fetchone() + + async def update_beginner_mission_date(self, user_id: int) -> None: + sql = profile.update(profile.c.user == user_id).values( + ad_confirm_date=func.now() + ) + result = await self.execute(sql) + if result is None: + self.logger.error(f"Failed to update user {user_id} yui medal date!") + + async def put_beginner_mission(self, user_id: int, beginner_mission_id: int, condition_id: int, is_seat: bool, achievement_num: int) -> Optional[int]: + pass + + async def complete_beginner_mission(self, user_id: int, condition_id: int) -> None: + pass + + async def reward_beginner_mission(self, user_id: int, condition_id: int) -> None: + pass + + async def get_beginner_missions(self, user_id: int) -> Optional[List[Row]]: + pass + + async def get_beginner_missions_by_mission_id(self, user_id: int, beginner_mission_id: int) -> Optional[List[Row]]: + pass + + async def get_beginner_mission(self, user_id: int, condition_id: int) -> Optional[Row]: + pass + + async def set_title(self, user_id: int, title_id: int) -> None: + result = await self.execute(profile.update(profile.c.user == user_id).values(setting_title_id=title_id)) + if not result: + self.logger.error(f"Failed to set user {user_id} title to {title_id}") diff --git a/titles/sao/schema/static.py b/titles/sao/schema/static.py index 8b5b1a1..7f4f679 100644 --- a/titles/sao/schema/static.py +++ b/titles/sao/schema/static.py @@ -1,81 +1,193 @@ -from typing import Dict, List, Optional -from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_ -from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, Float -from sqlalchemy.engine.base import Connection +from typing import Dict, List, Optional, Union +from sqlalchemy import Table, Column, UniqueConstraint, ForeignKey +from sqlalchemy.types import Integer, String, BIGINT, Boolean, INTEGER, VARCHAR, BOOLEAN, DECIMAL from sqlalchemy.engine import Row from sqlalchemy.schema import ForeignKey -from sqlalchemy.sql import func, select from sqlalchemy.dialects.mysql import insert from core.data.schema import BaseData, metadata +from core.data import cached quest = Table( "sao_static_quest", metadata, - Column("id", Integer, primary_key=True, nullable=False), - Column("version", Integer), - Column("questSceneId", Integer), - Column("sortNo", Integer), - Column("name", String(255)), - Column("enabled", Boolean), - UniqueConstraint( - "version", "questSceneId", name="sao_static_quest_uk" - ), - mysql_charset="utf8mb4", + Column("QuestSceneId", BIGINT, primary_key=True, nullable=False), + Column("SortNo", INTEGER, nullable=False), + Column("Tutorial", BOOLEAN, nullable=False), + Column("ColRate", DECIMAL, nullable=False), + Column("LimitDefault", INTEGER, nullable=False), + Column("LimitResurrection", INTEGER, nullable=False), + Column("RewardTableSubId", INTEGER, nullable=False), + Column("PlayerTraceTableSubId", INTEGER, nullable=False), + Column("SuccessPlayerExp", INTEGER, nullable=False), + Column("FailedPlayerExp", INTEGER, nullable=False), + Column("PairExpRate", INTEGER, nullable=False), + Column("TrioExpRate", INTEGER, nullable=False), + Column("SingleRewardVp", INTEGER, nullable=False), + Column("PairRewardVp", INTEGER, nullable=False), + Column("TrioRewardVp", INTEGER, nullable=False), + mysql_charset="utf8mb4" ) -hero = Table( - "sao_static_hero_list", +prop = Table( + "sao_static_property", metadata, - Column("id", Integer, primary_key=True, nullable=False), - Column("version", Integer), - Column("heroLogId", Integer), - Column("name", String(255)), - Column("nickname", String(255)), - Column("rarity", Integer), - Column("skillTableSubId", Integer), - Column("awakeningExp", Integer), - Column("flavorText", String(255)), - Column("enabled", Boolean), - UniqueConstraint( - "version", "heroLogId", name="sao_static_hero_list_uk" - ), - mysql_charset="utf8mb4", + Column("PropertyId", BIGINT, primary_key=True, nullable=False), + Column("PropertyTargetType", INTEGER, nullable=False), + Column("PropertyName", VARCHAR(255), nullable=False), + Column("PropertyName_en", VARCHAR(255)), + Column("PropertyNameFormat", VARCHAR(255), nullable=False), + Column("PropertyNameFormat_en", VARCHAR(255)), + Column("PropertyTypeId", INTEGER, nullable=False), + Column("Value1Min", INTEGER, nullable=False), + Column("Value1Max", INTEGER, nullable=False), + Column("Value2Min", INTEGER, nullable=False), + Column("Value2Max", INTEGER, nullable=False), + mysql_charset="utf8mb4" +) + +player_trace = Table( + "sao_static_trace_table", + metadata, + Column("PlayerTraceTableId", BIGINT, primary_key=True, nullable=False), + Column("PlayerTraceTableSubId", INTEGER, nullable=False), + Column("CommonRewardType", INTEGER, nullable=False), + Column("CommonRewardId", INTEGER, nullable=False), + Column("CommonRewardNum", INTEGER, nullable=False), + Column("Rate", INTEGER, nullable=False), + mysql_charset="utf8mb4" +) + +skill = Table( + "sao_static_skill", + metadata, + Column("SkillId", BIGINT, nullable=False, primary_key=True), + Column("WeaponTypeId", INTEGER, nullable=False), + Column("Name", VARCHAR(255), nullable=False), + Column("Name_en", VARCHAR(255)), + Column("Attack", BOOLEAN, nullable=False), + Column("Passive", BOOLEAN, nullable=False), + Column("Pet", BOOLEAN, nullable=False), + Column("Level", INTEGER, nullable=False), + Column("SkillCondition", INTEGER, nullable=False), + Column("CoolTime", INTEGER, nullable=False), + Column("SkillIcon", VARCHAR(255), nullable=False), + Column("FriendSkillIcon", VARCHAR(255), nullable=False), + Column("InfoText", VARCHAR(255), nullable=False), + Column("InfoText_en", VARCHAR(255)), + mysql_charset="utf8mb4" +) + +skill_table = Table( + "sao_static_skill_table", + metadata, + Column("SkillTableId", BIGINT, nullable=False, primary_key=True), + Column("SkillId", BIGINT, ForeignKey("sao_static_skill.SkillId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("SkillTableSubId", INTEGER, nullable=False), + Column("LevelObtained", INTEGER, nullable=False), # Level + Column("AwakeningId", INTEGER, nullable=False), + mysql_charset="utf8mb4" ) equipment = Table( "sao_static_equipment_list", metadata, - Column("id", Integer, primary_key=True, nullable=False), - Column("version", Integer), - Column("equipmentId", Integer), - Column("equipmentType", Integer), - Column("weaponTypeId", Integer), - Column("name", String(255)), - Column("rarity", Integer), - Column("flavorText", String(255)), - Column("enabled", Boolean), - UniqueConstraint( - "version", "equipmentId", name="sao_static_equipment_list_uk" - ), - mysql_charset="utf8mb4", + Column("EquipmentId", BIGINT, primary_key=True, nullable=False), + Column("EquipmentType", INTEGER, nullable=False), + Column("WeaponTypeId", INTEGER, nullable=False), + Column("Name", VARCHAR(255), nullable=False), + Column("Name_en", VARCHAR(255)), + Column("Rarity", INTEGER, nullable=False), + Column("Power", INTEGER, nullable=False), + Column("StrengthIncrement", INTEGER, nullable=False), + Column("SkillCondition", INTEGER, nullable=False), + Column("Property1PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property1Value1", INTEGER, nullable=False), + Column("Property1Value2", INTEGER, nullable=False), + Column("Property2PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property2Value1", INTEGER, nullable=False), + Column("Property2Value2", INTEGER, nullable=False), + Column("Property3PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property3Value1", INTEGER, nullable=False), + Column("Property3Value2", INTEGER, nullable=False), + Column("Property4PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property4Value1", INTEGER, nullable=False), + Column("Property4Value2", INTEGER, nullable=False), + Column("SalePrice", INTEGER, nullable=False), + Column("CompositionExp", INTEGER, nullable=False), + Column("AwakeningExp", INTEGER, nullable=False), + Column("FlavorText", VARCHAR(255), nullable=False), + Column("FlavorText_en", VARCHAR(255)), + mysql_charset="utf8mb4" +) + +hero = Table( + "sao_static_hero_list", + metadata, + Column("HeroLogId", BIGINT, primary_key=True, nullable=False), + Column("CharaId", INTEGER, nullable=False), + Column("Name", VARCHAR(255), nullable=False), + Column("Nickname", VARCHAR(255), nullable=False), + Column("Name_en", VARCHAR(255)), + Column("Nickname_en", VARCHAR(255)), + Column("Rarity", INTEGER, nullable=False), + Column("WeaponTypeId", INTEGER, nullable=False), + Column("HeroLogRoleId", INTEGER, nullable=False), + Column("CostumeTypeId", INTEGER, nullable=False), + Column("UnitId", INTEGER, nullable=False), + Column("DefaultEquipmentId1", BIGINT, ForeignKey("sao_static_equipment_list.EquipmentId", ondelete="cascade", onupdate="cascade")), + Column("DefaultEquipmentId2", BIGINT, ForeignKey("sao_static_equipment_list.EquipmentId", ondelete="cascade", onupdate="cascade")), + Column("SkillTableSubId", INTEGER, nullable=False), + Column("HpMin", INTEGER, nullable=False), + Column("HpMax", INTEGER, nullable=False), + Column("StrMin", INTEGER, nullable=False), + Column("StrMax", INTEGER, nullable=False), + Column("VitMin", INTEGER, nullable=False), + Column("VitMax", INTEGER, nullable=False), + Column("IntMin", INTEGER, nullable=False), + Column("IntMax", INTEGER, nullable=False), + Column("Property1PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property1Value1", INTEGER, nullable=False), + Column("Property1Value2", INTEGER, nullable=False), + Column("Property2PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property2Value1", INTEGER, nullable=False), + Column("Property2Value2", INTEGER, nullable=False), + Column("Property3PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property3Value1", INTEGER, nullable=False), + Column("Property3Value2", INTEGER, nullable=False), + Column("Property4PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property4Value1", INTEGER, nullable=False), + Column("Property4Value2", INTEGER, nullable=False), + Column("FlavorText", VARCHAR(255), nullable=False), + Column("FlavorText_en", VARCHAR(255)), + Column("SalePrice", INTEGER, nullable=False), + Column("CompositionExp", INTEGER, nullable=False), + Column("AwakeningExp", INTEGER, nullable=False), + Column("Slot4UnlockLevel", INTEGER, nullable=False), + Column("Slot5UnlockLevel", INTEGER, nullable=False), + Column("CollectionEmptyFrameDisplayFlag", BOOLEAN, nullable=False), + mysql_charset="utf8mb4" ) item = Table( "sao_static_item_list", metadata, - Column("id", Integer, primary_key=True, nullable=False), - Column("version", Integer), - Column("itemId", Integer), - Column("itemTypeId", Integer), - Column("name", String(255)), - Column("rarity", Integer), - Column("flavorText", String(255)), - Column("enabled", Boolean), - UniqueConstraint( - "version", "itemId", name="sao_static_item_list_uk" - ), - mysql_charset="utf8mb4", + Column("ItemId", INTEGER, nullable=False, primary_key=True), + Column("ItemTypeId", INTEGER, nullable=False), + Column("Name", VARCHAR(255), nullable=False), + Column("Name_en", VARCHAR(255)), + Column("Rarity", INTEGER, nullable=False), + Column("Value", INTEGER, nullable=False), + Column("PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("PropertyValue1Min", INTEGER, nullable=False), + Column("PropertyValue1Max", INTEGER, nullable=False), + Column("PropertyValue2Min", INTEGER, nullable=False), + Column("PropertyValue2Max", INTEGER, nullable=False), + Column("FlavorText", VARCHAR(255), nullable=False), + Column("FlavorText_en", VARCHAR(255)), + Column("SalePrice", INTEGER, nullable=False), + Column("ItemIcon", VARCHAR(255), nullable=False), + mysql_charset="utf8mb4" ) support = Table( @@ -93,7 +205,7 @@ support = Table( UniqueConstraint( "version", "supportLogId", name="sao_static_support_log_list_uk" ), - mysql_charset="utf8mb4", + mysql_charset="utf8mb4" ) rare_drop = Table( @@ -107,7 +219,7 @@ rare_drop = Table( UniqueConstraint( "version", "questRareDropId", "commonRewardId", name="sao_static_rare_drop_list_uk" ), - mysql_charset="utf8mb4", + mysql_charset="utf8mb4" ) title = Table( @@ -124,91 +236,361 @@ title = Table( UniqueConstraint( "version", "titleId", name="sao_static_title_list_uk" ), - mysql_charset="utf8mb4", + mysql_charset="utf8mb4" +) + +reward = Table( + "sao_static_reward", + metadata, + Column("RewardTableId", BIGINT, primary_key=True, nullable=False), + Column("RewardTableSubId", INTEGER, nullable=False), + Column("UnanalyzedLogGradeId", INTEGER, nullable=False), + Column("CommonRewardType", INTEGER, nullable=False), + Column("CommonRewardId", INTEGER, nullable=False), + Column("CommonRewardNum", INTEGER, nullable=False), + Column("StrengthMin", INTEGER, nullable=False), + Column("StrengthMax", INTEGER, nullable=False), + Column("PropertyTableSubId", INTEGER, nullable=False), + Column("QuestInfoDisplayFlag", BOOLEAN, nullable=False), + Column("Rate", INTEGER, nullable=False), + mysql_charset="utf8mb4" +) + +ex_bonus = Table( + "sao_static_ex_bonus", + metadata, + Column("ExBonusTableId", BIGINT, primary_key=True, nullable=False), + Column("ExBonusTableSubId", INTEGER, nullable=False), + Column("ExBonusConditionId", INTEGER, nullable=False), + Column("ConditionValue1", INTEGER, nullable=False), + Column("ConditionValue2", INTEGER, nullable=False), + Column("CommonRewardType", INTEGER, nullable=False), + Column("CommonRewardId", INTEGER, nullable=False), + Column("CommonRewardNum", INTEGER, nullable=False), + Column("Strength", INTEGER, nullable=False), + Column("Property1PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property1Value1", INTEGER, nullable=False), + Column("Property1Value2", INTEGER, nullable=False), + Column("Property2PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property2Value1", INTEGER, nullable=False), + Column("Property2Value2", INTEGER, nullable=False), + Column("Property3PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property3Value1", INTEGER, nullable=False), + Column("Property3Value2", INTEGER, nullable=False), + Column("Property4PropertyId", BIGINT, ForeignKey("sao_static_property.PropertyId", ondelete="cascade", onupdate="cascade"), nullable=False), + Column("Property4Value1", INTEGER, nullable=False), + Column("Property4Value2", INTEGER, nullable=False), + mysql_charset="utf8mb4" +) + +episode = Table( + "sao_static_episode", + metadata, + Column("EpisodeId", BIGINT, nullable=False, primary_key=True), + Column("EpisodeChapterId", INTEGER, nullable=False), + Column("ReleaseEpisodeId", INTEGER, nullable=False), + Column("Title", VARCHAR(255), nullable=False), + Column("CommentSummary", VARCHAR(255), nullable=False), + Column("ExBonusTableSubId", INTEGER, nullable=False), + Column("QuestSceneId", BIGINT, ForeignKey("sao_static_quest.QuestSceneId", ondelete="cascade", onupdate="cascade")), + mysql_charset="utf8mb4" +) + +tower = Table( + "sao_static_tower", + metadata, + Column("TowerId", BIGINT, nullable=False, primary_key=True), + Column("ReleaseTowerId", INTEGER, nullable=False), + Column("ExBonusTableSubId", INTEGER, nullable=False), + Column("QuestSceneId", BIGINT, ForeignKey("sao_static_quest.QuestSceneId", ondelete="cascade", onupdate="cascade"), nullable=False), + mysql_charset="utf8mb4" +) + +ex_tower = Table( + "sao_static_ex_tower", + metadata, + Column("ExTowerQuestId", BIGINT, nullable=False, primary_key=True), + Column("ExTowerId", INTEGER, nullable=False), + Column("ReleaseExTowerQuestId", INTEGER, nullable=False), + Column("Title", VARCHAR(255), nullable=False), + Column("Title_en", VARCHAR(255)), + Column("ExBonusTableSubId", INTEGER, nullable=False), + Column("QuestSceneId", BIGINT, ForeignKey("sao_static_quest.QuestSceneId", ondelete="cascade", onupdate="cascade"), nullable=False), + mysql_charset="utf8mb4" +) + +side_quest = Table( + "sao_static_side_quest", + metadata, + Column("SideQuestId", BIGINT, nullable=False, unique=True, primary_key=True), + Column("DisplayName", VARCHAR(255), nullable=False), + Column("DisplayName_en", VARCHAR(255)), + Column("EpisodeNum", INTEGER, nullable=False), + Column("ExBonusTableSubId", INTEGER, nullable=False), + Column("QuestSceneId", BIGINT, ForeignKey("sao_static_quest.QuestSceneId", ondelete="cascade", onupdate="cascade"), nullable=False), + mysql_charset="utf8mb4" ) class SaoStaticData(BaseData): - async def put_quest( self, questSceneId: int, version: int, sortNo: int, name: str, enabled: bool ) -> Optional[int]: + async def put_quest(self, data: Dict[str, str]) -> Optional[int]: sql = insert(quest).values( - questSceneId=questSceneId, - version=version, - sortNo=sortNo, - name=name, - enabled=enabled, + QuestSceneId=data["QuestSceneId"], + SortNo=data["SortNo"], + Tutorial=data["Tutorial"], + ColRate=data["ColRate"], + LimitDefault=data["LimitDefault"], + LimitResurrection=data["LimitResurrection"], + RewardTableSubId=data["RewardTableSubId"], + PlayerTraceTableSubId=data["PlayerTraceTableSubId"], + SuccessPlayerExp=data["SuccessPlayerExp"], + FailedPlayerExp=data["FailedPlayerExp"], + PairExpRate=data["PairExpRate"], + TrioExpRate=data["TrioExpRate"], + SingleRewardVp=data["SingleRewardVp"], + PairRewardVp=data["PairRewardVp"], + TrioRewardVp=data["TrioRewardVp"], ) - conflict = sql.on_duplicate_key_update( - name=name, questSceneId=questSceneId, version=version - ) + conflict = sql.on_duplicate_key_update(QuestSceneId=data["QuestSceneId"]) result = await self.execute(conflict) if result is None: return None - return result.lastrowid + return data["QuestSceneId"] - async def put_hero( self, version: int, heroLogId: int, name: str, nickname: str, rarity: int, skillTableSubId: int, awakeningExp: int, flavorText: str, enabled: bool ) -> Optional[int]: + async def put_hero(self, data: Dict[str, str]) -> Optional[int]: sql = insert(hero).values( - version=version, - heroLogId=heroLogId, - name=name, - nickname=nickname, - rarity=rarity, - skillTableSubId=skillTableSubId, - awakeningExp=awakeningExp, - flavorText=flavorText, - enabled=enabled + HeroLogId=data["HeroLogId"], + CharaId=data["CharaId"], + Name=data["Name"], + Nickname=data["Nickname"], + Rarity=data["Rarity"], + WeaponTypeId=data["WeaponTypeId"], + HeroLogRoleId=data["HeroLogRoleId"], + CostumeTypeId=data["CostumeTypeId"], + UnitId=data["UnitId"], + DefaultEquipmentId1=data["DefaultEquipmentId1"], + DefaultEquipmentId2=data["DefaultEquipmentId2"], + SkillTableSubId=data["SkillTableSubId"], + HpMin=data["HpMin"], + HpMax=data["HpMax"], + StrMin=data["StrMin"], + StrMax=data["StrMax"], + VitMin=data["VitMin"], + VitMax=data["VitMax"], + IntMin=data["IntMin"], + IntMax=data["IntMax"], + Property1PropertyId=data["Property1PropertyId"], + Property1Value1=data["Property1Value1"], + Property1Value2=data["Property1Value2"], + Property2PropertyId=data["Property2PropertyId"], + Property2Value1=data["Property2Value1"], + Property2Value2=data["Property2Value2"], + Property3PropertyId=data["Property3PropertyId"], + Property3Value1=data["Property3Value1"], + Property3Value2=data["Property3Value2"], + Property4PropertyId=data["Property4PropertyId"], + Property4Value1=data["Property4Value1"], + Property4Value2=data["Property4Value2"], + FlavorText=data["FlavorText"], + SalePrice=data["SalePrice"], + CompositionExp=data["CompositionExp"], + AwakeningExp=data["AwakeningExp"], + Slot4UnlockLevel=data["Slot4UnlockLevel"], + Slot5UnlockLevel=data["Slot5UnlockLevel"], + CollectionEmptyFrameDisplayFlag=data["CollectionEmptyFrameDisplayFlag"], ) - conflict = sql.on_duplicate_key_update( - name=name, heroLogId=heroLogId - ) + conflict = sql.on_duplicate_key_update(HeroLogId=data["HeroLogId"]) result = await self.execute(conflict) if result is None: return None - return result.lastrowid + return data["HeroLogId"] - async def put_equipment( self, version: int, equipmentId: int, name: str, equipmentType: int, weaponTypeId:int, rarity: int, flavorText: str, enabled: bool ) -> Optional[int]: + async def put_hero_translation(self, hero_id: int, name_en: str, nickname_en: str, flavor_text_en: str) -> None: + result = await self.execute(hero.update(hero.c.HeroLogId == hero_id).values( + Name=name_en, + Nickname=nickname_en, + FlavorText=flavor_text_en, + )) + + result = await self.execute(result) + if result is None: + self.logger.error(f"Failed to add translated text for hero {hero_id}") + + async def put_equipment(self, data: Dict[str, str]) -> Optional[int]: sql = insert(equipment).values( - version=version, - equipmentId=equipmentId, - name=name, - equipmentType=equipmentType, - weaponTypeId=weaponTypeId, - rarity=rarity, - flavorText=flavorText, - enabled=enabled + EquipmentId=data["EquipmentId"], + EquipmentType=data["EquipmentType"], + WeaponTypeId=data["WeaponTypeId"], + Name=data["Name"], + Rarity=data["Rarity"], + Power=data["Power"], + StrengthIncrement=data["StrengthIncrement"], + SkillCondition=data["SkillCondition"], + Property1PropertyId=data["Property1PropertyId"], + Property1Value1=data["Property1Value1"], + Property1Value2=data["Property1Value2"], + Property2PropertyId=data["Property2PropertyId"], + Property2Value1=data["Property2Value1"], + Property2Value2=data["Property2Value2"], + Property3PropertyId=data["Property3PropertyId"], + Property3Value1=data["Property3Value1"], + Property3Value2=data["Property3Value2"], + Property4PropertyId=data["Property4PropertyId"], + Property4Value1=data["Property4Value1"], + Property4Value2=data["Property4Value2"], + SalePrice=data["SalePrice"], + CompositionExp=data["CompositionExp"], + AwakeningExp=data["AwakeningExp"], + FlavorText=data["FlavorText"], ) - conflict = sql.on_duplicate_key_update( - name=name, equipmentId=equipmentId - ) + conflict = sql.on_duplicate_key_update(EquipmentId=data["EquipmentId"]) result = await self.execute(conflict) if result is None: return None - return result.lastrowid + return data["EquipmentId"] - async def put_item( self, version: int, itemId: int, name: str, itemTypeId: int, rarity: int, flavorText: str, enabled: bool ) -> Optional[int]: + async def put_equipment_translation(self, equip_id: int, name_en: str, flavor_text_en: str) -> None: + result = await self.execute(equipment.update(equipment.c.EquipmentId == equip_id).values( + Name_en=name_en, + FlavorText_en=flavor_text_en, + )) + + result = await self.execute(result) + if result is None: + self.logger.error(f"Failed to add translated text for equipment {equip_id}") + + async def put_item(self, data: Dict[str, str]) -> Optional[int]: sql = insert(item).values( - version=version, - itemId=itemId, - name=name, - itemTypeId=itemTypeId, - rarity=rarity, - flavorText=flavorText, - enabled=enabled + ItemId=data["ItemId"], + ItemTypeId=data["ItemTypeId"], + Name=data["Name"], + Rarity=data["Rarity"], + Value=data["Value"], + PropertyId=data["PropertyId"], + PropertyValue1Min=data["PropertyValue1Min"], + PropertyValue1Max=data["PropertyValue1Max"], + PropertyValue2Min=data["PropertyValue2Min"], + PropertyValue2Max=data["PropertyValue2Max"], + FlavorText=data["FlavorText"], + SalePrice=data["SalePrice"], + ItemIcon=data["ItemIcon"], + ) - conflict = sql.on_duplicate_key_update( - name=name, itemId=itemId - ) + conflict = sql.on_duplicate_key_update(ItemId=data["ItemId"]) result = await self.execute(conflict) if result is None: return None - return result.lastrowid + return data["ItemId"] + + async def put_item_translation(self, item_id: int, name_en: str, flavor_text_en: str) -> None: + result = await self.execute(item.update(item.c.ItemId == item_id).values( + Name_en=name_en, + FlavorText_en=flavor_text_en, + )) + + result = await self.execute(result) + if result is None: + self.logger.error(f"Failed to add translated text for item {item_id}") + + async def put_property(self, data: Dict[str, str]) -> None: + sql = insert(prop).values( + PropertyId=data["PropertyId"], + PropertyTargetType=data["PropertyTargetType"], + PropertyName=data["PropertyName"], + PropertyNameFormat=data["PropertyNameFormat"], + PropertyTypeId=data["PropertyTypeId"], + Value1Min=data["Value1Min"], + Value1Max=data["Value1Max"], + Value2Min=data["Value2Min"], + Value2Max=data["Value2Max"], + ) + + conflict = sql.on_duplicate_key_update(PropertyId=data["PropertyId"]) + + result = await self.execute(conflict) + if result is None: + return None + return data["PropertyId"] + async def put_property_translation(self, property_id: int, name_en: str, name_fmt: str) -> None: + result = await self.execute(prop.update(prop.c.PropertyId == property_id).values( + Name_en=name_en, + PropertyNameFormat_en=name_fmt, + )) + + result = await self.execute(result) + if result is None: + self.logger.error(f"Failed to add translated text for property {property_id}") + + async def put_skill(self, data: Dict[str, str]) -> None: + sql = insert(skill).values( + SkillId=data["SkillId"], + WeaponTypeId=data["WeaponTypeId"], + Name=data["Name"], + Attack=data["Attack"], + Passive=data["Passive"], + Pet=data["Pet"], + Level=data["Level"], + SkillCondition=data["SkillCondition"], + CoolTime=data["CoolTime"], + SkillIcon=data["SkillIcon"], + FriendSkillIcon=data["FriendSkillIcon"], + InfoText=data["InfoText"], + ) + + conflict = sql.on_duplicate_key_update(SkillId=data["SkillId"]) + + result = await self.execute(conflict) + if result is None: + return None + return data["SkillId"] + + async def put_skill_translation(self, skill_id: int, name_en: str, info_txt: str) -> None: + result = await self.execute(skill.update(skill.c.SkillId == skill_id).values( + Name_en=name_en, + InfoText_en=info_txt, + )) + + result = await self.execute(result) + if result is None: + self.logger.error(f"Failed to add translated text for skill {skill_id}") + + async def put_skill_table(self, skill_id: int, sub_id: int, level: int, awakening: int, table_id: int) -> None: + sql = insert(skill_table).values( + SkillTableId=table_id, + SkillId=skill_id, + SkillTableSubId=sub_id, + LevelObtained=level, + AwakeningId=awakening, + ) + conflict = sql.on_duplicate_key_update(SkillTableId=table_id) + result = await self.execute(conflict) + if result is None: + self.logger.error(f"Failed to add skill table {skill_id}") + + async def put_player_trace(self, data: Dict[str, str]) -> None: + sql = insert(player_trace).values( + PlayerTraceTableId=data["PlayerTraceTableId"], + PlayerTraceTableSubId=data["PlayerTraceTableSubId"], + CommonRewardType=data["CommonRewardType"], + CommonRewardId=data["CommonRewardId"], + CommonRewardNum=data["CommonRewardNum"], + Rate=data["Rate"], + ) + + conflict = sql.on_duplicate_key_update(PlayerTraceTableId=data["PlayerTraceTableId"]) + result = await self.execute(conflict) + if result is None: + return None + return data["PlayerTraceTableId"] + async def put_support_log( self, version: int, supportLogId: int, charaId: int, name: str, rarity: int, salePrice: int, skillName: str, enabled: bool ) -> Optional[int]: sql = insert(support).values( version=version, @@ -221,14 +603,12 @@ class SaoStaticData(BaseData): enabled=enabled ) - conflict = sql.on_duplicate_key_update( - name=name, supportLogId=supportLogId - ) + conflict = sql.on_duplicate_key_update(version=version) result = await self.execute(conflict) - if result is None: - return None - return result.lastrowid + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert support log {supportLogId}!") async def put_rare_drop( self, version: int, questRareDropId: int, commonRewardId: int, enabled: bool ) -> Optional[int]: sql = insert(rare_drop).values( @@ -238,14 +618,12 @@ class SaoStaticData(BaseData): enabled=enabled, ) - conflict = sql.on_duplicate_key_update( - questRareDropId=questRareDropId, commonRewardId=commonRewardId, version=version - ) + conflict = sql.on_duplicate_key_update(version=version) result = await self.execute(conflict) - if result is None: - return None - return result.lastrowid + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert rare drop {questRareDropId}!") async def put_title( self, version: int, titleId: int, displayName: str, requirement: int, rank: int, imageFilePath: str, enabled: bool ) -> Optional[int]: sql = insert(title).values( @@ -258,71 +636,141 @@ class SaoStaticData(BaseData): enabled=enabled ) - conflict = sql.on_duplicate_key_update( - displayName=displayName, titleId=titleId + conflict = sql.on_duplicate_key_update(version=version) + + result = await self.execute(conflict) + if result: + return result.inserted_primary_key['id'] + self.logger.error(f"Failed to insert title {titleId}") + + async def put_reward_table(self, data: Dict[str, Union[str, bool]]) -> None: + sql = insert(reward).values( + **data ) + conflict = sql.on_duplicate_key_update(**data) + result = await self.execute(conflict) if result is None: return None - return result.lastrowid + return data['RewardTableId'] + + async def put_ex_bonus(self, data: Dict[str, int]) -> None: + sql = insert(ex_bonus).values( + **data + ) - async def get_quests_id(self, sortNo: int) -> Optional[Dict]: - sql = quest.select(quest.c.sortNo == sortNo) - - result = await self.execute(sql) + conflict = sql.on_duplicate_key_update(**data) + + result = await self.execute(conflict) + if result is None: + return None + return data['ExBonusTableId'] + + async def put_episode(self, data: Dict[str, int]) -> None: + sql = insert(episode).values( + EpisodeId=data['EpisodeId'], + EpisodeChapterId=data['EpisodeChapterId'], + ReleaseEpisodeId=data['ReleaseEpisodeId'], + Title=data['Title'], + CommentSummary=data['CommentSummary'], + ExBonusTableSubId=data['ExBonusTableSubId'], + QuestSceneId=data['QuestSceneId'] if data['QuestSceneId'] > 0 else None, + ) + + conflict = sql.on_duplicate_key_update(EpisodeId=data['EpisodeId']) + + result = await self.execute(conflict) + if result is None: + return None + return data['EpisodeId'] + + async def put_tower(self, data: Dict[str, int]) -> None: + sql = insert(tower).values( + TowerId=data['TrialTowerId'], + ReleaseTowerId=data['ReleaseTrialTowerId'], + ExBonusTableSubId=data['ExBonusTableSubId'], + QuestSceneId=data['QuestSceneId'], + ) + + conflict = sql.on_duplicate_key_update(TowerId=data['TrialTowerId']) + + result = await self.execute(conflict) + if result is None: + return None + return data['TrialTowerId'] + + async def put_ex_tower(self, data: Dict[str, int]) -> None: + sql = insert(ex_tower).values( + ExTowerQuestId=data['ExTowerQuestId'], + ExTowerId=data['ExTowerId'], + ReleaseExTowerQuestId=data['ReleaseExTowerQuestId'], + Title=data['Title'], + ExBonusTableSubId=data['ExBonusTableSubId'], + QuestSceneId=data['QuestSceneId'], + ) + + conflict = sql.on_duplicate_key_update(ExTowerQuestId=data['ExTowerQuestId']) + + result = await self.execute(conflict) + if result is None: + return None + return data['ExTowerQuestId'] + + async def put_side_quest(self, data: Dict[str, int]) -> None: + sql = insert(side_quest).values( + SideQuestId=data['SideQuestId'], + DisplayName=data['DisplayName'], + EpisodeNum=data['EpisodeNum'], + ExBonusTableSubId=data['ExBonusTableSubId'], + QuestSceneId=data['QuestSceneId'], + ) + + conflict = sql.on_duplicate_key_update(SideQuestId=data['SideQuestId']) + + result = await self.execute(conflict) + if result is None: + return None + return data['SideQuestId'] + + async def get_quest_by_id(self, quest_scene_id: int) -> Optional[Row]: + result = await self.execute(quest.select(quest.c.QuestSceneId == quest_scene_id)) if result is None: return None return result.fetchone() - async def get_quests_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: - sql = quest.select(quest.c.version == version and quest.c.enabled == enabled).order_by( - quest.c.questSceneId.asc() - ) - - result = await self.execute(sql) + async def get_quests(self) -> Optional[List[Row]]: + result = await self.execute(quest.select()) if result is None: return None - return [list[2] for list in result.fetchall()] + return result.fetchall() - async def get_hero_id(self, heroLogId: int) -> Optional[Dict]: - sql = hero.select(hero.c.heroLogId == heroLogId) - - result = await self.execute(sql) + async def get_hero_by_id(self, heroLogId: int) -> Optional[Row]: + result = await self.execute(hero.select(hero.c.HeroLogId == heroLogId)) if result is None: return None return result.fetchone() - async def get_hero_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: - sql = hero.select(hero.c.version == version and hero.c.enabled == enabled).order_by( - hero.c.heroLogId.asc() - ) - - result = await self.execute(sql) + async def get_heros(self) -> Optional[List[Row]]: + result = await self.execute(hero.select()) if result is None: return None - return [list[2] for list in result.fetchall()] + return result.fetchall() - async def get_equipment_id(self, equipmentId: int) -> Optional[Dict]: - sql = equipment.select(equipment.c.equipmentId == equipmentId) - - result = await self.execute(sql) + async def get_equipment_by_id(self, equipmentId: int) -> Optional[Dict]: + result = await self.execute(equipment.select(equipment.c.EquipmentId == equipmentId)) if result is None: return None return result.fetchone() - async def get_equipment_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: - sql = equipment.select(equipment.c.version == version and equipment.c.enabled == enabled).order_by( - equipment.c.equipmentId.asc() - ) - - result = await self.execute(sql) + async def get_equipment(self) -> Optional[List[Dict]]: + result = await self.execute(equipment.select()) if result is None: return None - return [list[2] for list in result.fetchall()] + return result.fetchall() - async def get_item_id(self, itemId: int) -> Optional[Dict]: - sql = item.select(item.c.itemId == itemId) + async def get_item_id(self, item_id: int) -> Optional[Dict]: + sql = item.select(item.c.ItemId == item_id) result = await self.execute(sql) if result is None: @@ -347,6 +795,12 @@ class SaoStaticData(BaseData): return None return [list[2] for list in result.fetchall()] + async def get_support_logs(self) -> Optional[List[Dict]]: + result = await self.execute(support.select()) + if result is None: + return None + return result.fetchall() + async def get_support_log_ids(self, version: int, enabled: bool) -> Optional[List[Dict]]: sql = support.select(support.c.version == version and support.c.enabled == enabled).order_by( support.c.supportLogId.asc() @@ -365,4 +819,156 @@ class SaoStaticData(BaseData): result = await self.execute(sql) if result is None: return None - return [list[2] for list in result.fetchall()] \ No newline at end of file + return [list[2] for list in result.fetchall()] + + async def get_reward_by_table(self, table_id: int) -> Optional[Row]: + result = await self.execute(reward.select(reward.c.RewardTableId == table_id)) + if result is None: + return None + return result.fetchone() + + async def get_rewards_by_subtable(self, subtable_id: int) -> Optional[List[Row]]: + result = await self.execute(reward.select(reward.c.RewardTableSubId == subtable_id)) + if result is None: + return None + return result.fetchall() + + async def get_rewards(self) -> Optional[List[Row]]: + result = await self.execute(reward.select()) + if result is None: + return None + return result.fetchall() + + async def get_ex_bonus_by_table(self, table_id: int) -> Optional[Row]: + result = await self.execute(ex_bonus.select(ex_bonus.c.ExBonusTableId == table_id)) + if result is None: + return None + return result.fetchone() + + async def get_ex_bonuses_by_subtable(self, subtable_id: int) -> Optional[List[Row]]: + result = await self.execute(ex_bonus.select(ex_bonus.c.ExBonusTableSubId == subtable_id)) + if result is None: + return None + return result.fetchall() + + async def get_ex_bonuses(self) -> Optional[List[Row]]: + result = await self.execute(ex_bonus.select()) + if result is None: + return None + return result.fetchall() + + async def get_episode_by_id(self, episode_id: int) -> Optional[Row]: + result = await self.execute(episode.select(episode.c.EpisodeId == episode_id)) + if result is None: + return None + return result.fetchone() + + async def get_episode_by_quest_id(self, quest_id: int) -> Optional[Row]: + result = await self.execute(episode.select(episode.c.QuestSceneId == quest_id)) + if result is None: + return None + return result.fetchone() + + async def get_episodes(self) -> Optional[List[Row]]: + result = await self.execute(episode.select()) + if result is None: + return None + return result.fetchall() + + async def get_tower_by_id(self, tower_id: int) -> Optional[Row]: + result = await self.execute(tower.select(tower.c.TowerId == tower_id)) + if result is None: + return None + return result.fetchone() + + async def get_tower_by_quest_id(self, quest_id: int) -> Optional[Row]: + result = await self.execute(tower.select(tower.c.QuestSceneId == quest_id)) + if result is None: + return None + return result.fetchone() + + async def get_towers(self) -> Optional[List[Row]]: + result = await self.execute(tower.select()) + if result is None: + return None + return result.fetchall() + + async def get_ex_tower_by_id(self, ex_tower_id: int) -> Optional[Row]: + result = await self.execute(ex_tower.select(ex_tower.c.ExTowerQuestId == ex_tower_id)) + if result is None: + return None + return result.fetchone() + + async def get_ex_tower_by_quest_id(self, quest_id: int) -> Optional[Row]: + result = await self.execute(ex_tower.select(ex_tower.c.QuestSceneId == quest_id)) + if result is None: + return None + return result.fetchone() + + async def get_ex_towers(self) -> Optional[List[Row]]: + result = await self.execute(ex_tower.select()) + if result is None: + return None + return result.fetchall() + + async def get_ex_towers_by_tower(self, ex_tower_id: int) -> Optional[List[Row]]: + result = await self.execute(ex_tower.select(ex_tower.c.ExTowerId == ex_tower_id)) + if result is None: + return None + return result.fetchall() + + async def get_side_quest_by_id(self, side_quest_id: int) -> Optional[Row]: + result = await self.execute(side_quest.select(side_quest.c.SideQuestId == side_quest_id)) + if result is None: + return None + return result.fetchone() + + async def get_side_quest_by_quest_id(self, quest_id: int) -> Optional[Row]: + result = await self.execute(side_quest.select(side_quest.c.QuestSceneId == quest_id)) + if result is None: + return None + return result.fetchone() + + async def get_side_quests(self) -> Optional[List[Row]]: + result = await self.execute(side_quest.select()) + if result is None: + return None + return result.fetchall() + + async def get_side_quests_by_episode(self, episode_num: int) -> Optional[List[Row]]: + result = await self.execute(side_quest.select(side_quest.c.EpisodeNum == episode_num)) + if result is None: + return None + return result.fetchall() + + async def get_player_trace_by_id(self, trace_id: int) -> Optional[Row]: + result = await self.execute(player_trace.select(player_trace.c.PlayerTraceTableId == trace_id)) + if result is None: + return None + return result.fetchone() + + async def get_player_trace_by_subid(self, trace_sub_id: int) -> Optional[List[Row]]: + result = await self.execute(player_trace.select(player_trace.c.PlayerTraceTableSubId == trace_sub_id)) + if result is None: + return None + return result.fetchall() + + async def get_player_traces(self) -> Optional[List[Row]]: + result = await self.execute(player_trace.select()) + if result is None: + return None + return result.fetchall() + + async def get_skill_table_by_subid(self, table_sub_id: int) -> Optional[List[Row]]: + result = await self.execute(skill_table.select(skill_table.c.SkillTableSubId == table_sub_id)) + + if result is None: + return None + return result.fetchall() + + async def get_skill_by_id(self, skill_id: int) -> Optional[Row]: + result = await self.execute(skill.select(skill.c.SkillId == skill_id)) + + if result is None: + return None + return result.fetchone() diff --git a/titles/sao/templates/sao_index.jinja b/titles/sao/templates/sao_index.jinja new file mode 100644 index 0000000..35d42ce --- /dev/null +++ b/titles/sao/templates/sao_index.jinja @@ -0,0 +1,182 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} +

Sword Art Online Arcade

+{% if profile is defined and profile is not none and profile.id > 0 %} + + + +

Profile for {{ profile.nick_name }} 

+{% include "core/templates/widgets/err_banner.jinja" %} +{% include "core/templates/widgets/succ_banner.jinja" %} + +

Register Hero Card

+{% if all_heros is defined and all_heros|length > 0%} +If you have cards printed on the official network, they must be registered here in order to work with the game.
+Only hero cards are supported at this time. If a card was registered incorrectly, contact a sysadmin.
+
+ +

+ +{% else %} +Card registration is not set up on this server. Please contact a sysadmin. +{% endif %} +{% elif sesh is defined and sesh is not none and sesh.user_id > 0 %} + +No profile information found for this account. + + +{% else %} +Login to view profile information. +{% endif %} +{% endblock content %} \ No newline at end of file