#pragma once #include #include #include #include "QuestMetadata.hh" #include "StaticGameData.hh" #include "Text.hh" #include "Version.hh" struct PSOQuestHeaderDCNTE { /* 0000 */ le_uint32_t text_offset = 0; /* 0004 */ le_uint32_t label_table_offset = 0; /* 0008 */ le_uint32_t size = 0; /* 000C */ le_uint16_t unknown_a1 = 0xFFFF; /* 000E */ le_uint16_t unknown_a2 = 0xFFFF; /* 0010 */ pstring name; /* 0020 */ } __packed_ws__(PSOQuestHeaderDCNTE, 0x20); struct PSOQuestHeaderDC112000 { /* 0000 */ le_uint32_t text_offset = 0; /* 0004 */ le_uint32_t label_table_offset = 0; /* 0008 */ le_uint32_t size = 0; /* 000C */ le_uint16_t unknown_a1 = 0xFFFF; /* 000E */ le_uint16_t unknown_a2 = 0xFFFF; /* 0010 */ pstring name; /* 0030 */ pstring short_description; /* 00B0 */ pstring long_description; /* 01D0 */ } __packed_ws__(PSOQuestHeaderDC112000, 0x1D0); struct PSOQuestHeaderDC { // Same format for DC v1 and v2 /* 0000 */ le_uint32_t text_offset = 0; /* 0004 */ le_uint32_t label_table_offset = 0; /* 0008 */ le_uint32_t size = 0; /* 000C */ le_uint16_t unknown_a1 = 0xFFFF; /* 000E */ le_uint16_t unknown_a2 = 0xFFFF; /* 0010 */ Language language = Language::JAPANESE; /* 0011 */ uint8_t unknown_a3 = 0; /* 0012 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests /* 0014 */ pstring name; /* 0034 */ pstring short_description; /* 00B4 */ pstring long_description; /* 01D4 */ } __packed_ws__(PSOQuestHeaderDC, 0x1D4); struct PSOQuestHeaderPC { /* 0000 */ le_uint32_t text_offset; /* 0004 */ le_uint32_t label_table_offset; /* 0008 */ le_uint32_t size = 0; /* 000C */ le_uint16_t unknown_a1 = 0xFFFF; /* 000E */ le_uint16_t unknown_a2 = 0xFFFF; /* 0010 */ Language language = Language::JAPANESE; /* 0011 */ uint8_t unknown_a3 = 0; /* 0012 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests /* 0014 */ pstring name; /* 0054 */ pstring short_description; /* 0154 */ pstring long_description; /* 0394 */ } __packed_ws__(PSOQuestHeaderPC, 0x394); struct PSOQuestHeaderV3 { /* 0000 */ le_uint32_t text_offset = 0; /* 0004 */ le_uint32_t label_table_offset = 0; /* 0008 */ le_uint32_t size = 0; /* 000C */ le_uint16_t unknown_a1 = 0xFFFF; /* 000E */ le_uint16_t unknown_a2 = 0xFFFF; /* 0010 */ Language language = Language::JAPANESE; /* 0011 */ uint8_t unknown_a3 = 0; // Note: The GC client byteswaps this field, then loads it as a byte, so // technically the high byte of this is what the client uses as the quest // number. In practice, this only matters if the quest runs send_statistic // without running prepare_statistic first, which is not the intended usage. /* 0012 */ le_uint16_t quest_number = 0; /* 0014 */ pstring name; /* 0034 */ pstring short_description; /* 00B4 */ pstring long_description; /* 01D4 */ } __packed_ws__(PSOQuestHeaderV3, 0x1D4); struct CreateItemMaskEntry { parray data1_fields; le_uint32_t present = 0; le_uint32_t unknown_a3 = 0; bool is_valid() const { return (this->data1_fields[0] || this->data1_fields[1] || this->data1_fields[2]); } CreateItemMaskEntry() = default; CreateItemMaskEntry(const QuestMetadata::CreateItemMask& mask); operator QuestMetadata::CreateItemMask() const; } __packed_ws__(CreateItemMaskEntry, 0x38); // Some quest authoring tools don't generate the full quest header, hence the // split structure here. struct PSOQuestHeaderBBBase { /* 0000 */ le_uint32_t text_offset = 0; /* 0004 */ le_uint32_t label_table_offset = 0; /* 0008 */ le_uint32_t size = 0; /* 000C */ le_uint16_t unknown_a1 = 0xFFFF; /* 000E */ le_uint16_t unknown_a2 = 0xFFFF; /* 0010 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests /* 0012 */ le_uint16_t unknown_a6 = 0; /* 0014 */ uint8_t episode = 0; // 0 = Ep1, 1 = Ep2, 2 = Ep4 /* 0015 */ uint8_t max_players = 0; // 0 means no limit (that is, 4) /* 0016 */ uint8_t joinable = 0; /* 0017 */ uint8_t unknown_a4 = 0; /* 0018 */ pstring name; /* 0058 */ pstring short_description; /* 0158 */ pstring long_description; /* 0398 */ } __packed_ws__(PSOQuestHeaderBBBase, 0x0398); struct PSOQuestHeaderBB : PSOQuestHeaderBBBase { struct FloorAssignment { // These fields match the bb_map_designate arguments (see QuestScript.cc). // Unused AreaAssignment structures should have all fields set to 0xFF. uint8_t floor = 0xFF; uint8_t area = 0xFF; uint8_t type = 0xFF; uint8_t layout_var = 0xFF; uint8_t entities_var = 0xFF; parray unused = 0xFF; } __packed_ws__(FloorAssignment, 8); /* 0398 */ le_uint32_t unknown_a5; /* 039C */ parray solo_unlock_flags; /* 03AC */ parray floor_assignments; /* 042C */ parray create_item_mask_entries; /* 122C */ } __packed_ws__(PSOQuestHeaderBB, 0x122C); void check_quest_opcode_definitions(); Episode episode_for_quest_episode_number(uint8_t episode_number); std::string disassemble_quest_script( const void* bin_data, // Must not be null size_t bin_size, Version version, Language language = Language::UNKNOWN, std::shared_ptr dat = nullptr, bool reassembly_mode = false, bool use_qedit_names = false); struct AssembledQuestScript { std::string data; QuestMetadata meta; }; AssembledQuestScript assemble_quest_script( const std::string& text, const std::vector& script_include_directories, const std::vector& native_include_directories, bool strict = true); void populate_quest_metadata_from_script(QuestMetadata& meta, const void* data, size_t size, Version version, Language language);