From 45332299b201a15084f674a3c3451a5bfdd886b4 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Sat, 5 Oct 2024 19:16:39 +0200 Subject: [PATCH] [UI] Added played game list and achievements UI. This will be functional after GPD & SPA implementation, for now shows info about currently booted title --- src/xenia/kernel/xam/achievement_manager.cc | 41 +- src/xenia/kernel/xam/achievement_manager.h | 66 +-- src/xenia/kernel/xam/xam_ui.cc | 436 +++++++++++++++++++- src/xenia/ui/imgui_drawer.cc | 44 +- src/xenia/ui/imgui_drawer.h | 20 +- src/xenia/ui/resources.h | 335 +++++++++++++++ 6 files changed, 903 insertions(+), 39 deletions(-) diff --git a/src/xenia/kernel/xam/achievement_manager.cc b/src/xenia/kernel/xam/achievement_manager.cc index 5100b154b..07ce9b54d 100644 --- a/src/xenia/kernel/xam/achievement_manager.cc +++ b/src/xenia/kernel/xam/achievement_manager.cc @@ -17,10 +17,11 @@ DEFINE_bool(show_achievement_notification, false, "Show achievement notification on screen.", "UI"); -DEFINE_string(default_achievement_backend, "", - "Defines which achievement backend should be used as an default. " - "Possible options: [].", - "Achievements"); +DEFINE_string( + default_achievements_backend, "GPD", + "Defines which achievements backend should be used as an default. " + "Possible options: GPD.", + "Kernel"); DECLARE_int32(user_language); @@ -49,8 +50,11 @@ void GpdAchievementBackend::EarnAchievement(const uint64_t xuid, achievement->achievement_name.c_str()))); const uint64_t unlock_time = Clock::QueryHostSystemTime(); - achievement->flags = - achievement->flags | static_cast(AchievementFlags::kAchieved); + // We're adding achieved online flag because on console locally achieved + // entries don't have valid unlock time. + achievement->flags = achievement->flags | + static_cast(AchievementFlags::kAchieved) | + static_cast(AchievementFlags::kAchievedOnline); achievement->unlock_time.high_part = static_cast(unlock_time >> 32); achievement->unlock_time.low_part = static_cast(unlock_time); @@ -206,6 +210,31 @@ AchievementManager::GetTitleAchievements(const uint64_t xuid, return default_achievements_backend_->GetTitleAchievements(xuid, title_id); } +const std::optional +AchievementManager::GetTitleAchievementsInfo(const uint64_t xuid, + const uint32_t title_id) const { + TitleAchievementsProfileInfo info = {}; + + const auto achievements = GetTitleAchievements(xuid, title_id); + + if (!achievements) { + return std::nullopt; + } + + info.achievements_count = static_cast(achievements->size()); + + for (const auto& entry : *achievements) { + if (!entry.IsUnlocked()) { + continue; + } + + info.unlocked_achievements_count++; + info.gamerscore += entry.gamerscore; + } + + return info; +} + bool AchievementManager::DoesAchievementExist( const uint32_t achievement_id) const { const util::XdbfGameData title_xdbf = kernel_state()->title_xdbf(); diff --git a/src/xenia/kernel/xam/achievement_manager.h b/src/xenia/kernel/xam/achievement_manager.h index ca896db65..037b9c879 100644 --- a/src/xenia/kernel/xam/achievement_manager.h +++ b/src/xenia/kernel/xam/achievement_manager.h @@ -11,6 +11,7 @@ #define XENIA_KERNEL_XAM_ACHIEVEMENT_MANAGER_H_ #include +#include #include #include @@ -21,6 +22,34 @@ namespace xe { namespace kernel { namespace xam { +enum class AchievementType : uint32_t { + kCompletion = 1, + kLeveling = 2, + kUnlock = 3, + kEvent = 4, + kTournament = 5, + kCheckpoint = 6, + kOther = 7, +}; + +enum class AchievementPlatform : uint32_t { + kX360 = 0x100000, + kPC = 0x200000, + kMobile = 0x300000, + kWebGames = 0x400000, +}; + +enum class AchievementFlags : uint32_t { + kTypeMask = 0x7, + kShowUnachieved = 0x8, + kAchievedOnline = 0x10000, + kAchieved = 0x20000, + kNotAchievable = 0x40000, + kWasNotAchievable = 0x80000, + kPlatformMask = 0x700000, + kColorizable = 0x1000000, // avatar awards only? +}; + struct X_ACHIEVEMENT_UNLOCK_TIME { xe::be high_part; xe::be low_part; @@ -81,34 +110,17 @@ struct AchievementGpdStructure { std::u16string achievement_name; std::u16string unlocked_description; std::u16string locked_description; + + bool IsUnlocked() const { + return (flags & static_cast(AchievementFlags::kAchieved)) || + flags & static_cast(AchievementFlags::kAchievedOnline); + } }; -enum class AchievementType : uint32_t { - kCompletion = 1, - kLeveling = 2, - kUnlock = 3, - kEvent = 4, - kTournament = 5, - kCheckpoint = 6, - kOther = 7, -}; - -enum class AchievementPlatform : uint32_t { - kX360 = 0x100000, - kPC = 0x200000, - kMobile = 0x300000, - kWebGames = 0x400000, -}; - -enum class AchievementFlags : uint32_t { - kTypeMask = 0x7, - kShowUnachieved = 0x8, - kAchievedOnline = 0x10000, - kAchieved = 0x20000, - kNotAchievable = 0x40000, - kWasNotAchievable = 0x80000, - kPlatformMask = 0x700000, - kColorizable = 0x1000000, // avatar awards only? +struct TitleAchievementsProfileInfo { + uint32_t achievements_count; + uint32_t unlocked_achievements_count; + uint32_t gamerscore; }; class AchievementBackendInterface { @@ -183,6 +195,8 @@ class AchievementManager { const uint32_t achievement_id) const; const std::vector* GetTitleAchievements( const uint64_t xuid, const uint32_t title_id) const; + const std::optional GetTitleAchievementsInfo( + const uint64_t xuid, const uint32_t title_id) const; private: bool DoesAchievementExist(const uint32_t achievement_id) const; diff --git a/src/xenia/kernel/xam/xam_ui.cc b/src/xenia/kernel/xam/xam_ui.cc index c4edd2a70..813066132 100644 --- a/src/xenia/kernel/xam/xam_ui.cc +++ b/src/xenia/kernel/xam/xam_ui.cc @@ -8,6 +8,7 @@ */ #include "third_party/imgui/imgui.h" +#include "xenia/app/profile_dialogs.h" #include "xenia/base/logging.h" #include "xenia/base/string_util.h" #include "xenia/base/system.h" @@ -25,6 +26,8 @@ #include "xenia/ui/windowed_app_context.h" #include "xenia/xbox.h" +#include "third_party/fmt/include/fmt/chrono.h" + DEFINE_bool(storage_selection_dialog, false, "Show storage device selection dialog when the game requests it.", "UI"); @@ -54,6 +57,8 @@ namespace xam { extern std::atomic xam_dialogs_shown_; +constexpr ImVec2 default_image_icon_size = ImVec2(75.f, 75.f); + class XamDialog : public xe::ui::ImGuiDialog { public: void set_close_callback(std::function close_callback) { @@ -381,6 +386,404 @@ class GamertagModifyDialog final : public ui::ImGuiDialog { ProfileManager* profile_manager_; }; +struct AchievementInfo { + uint32_t id; + std::u16string name; + std::u16string desc; + std::u16string unachieved; + uint32_t gamerscore; + uint32_t image_id; + uint32_t flags; + std::chrono::system_clock::time_point unlock_time; + + bool IsUnlocked() const { + return (flags & static_cast(AchievementFlags::kAchieved)) || + flags & static_cast(AchievementFlags::kAchievedOnline); + } + + // Unlocked online means that unlock time is confirmed and valid! + bool IsUnlockedOnline() const { + return (flags & static_cast(AchievementFlags::kAchievedOnline)); + } +}; + +struct TitleInfo { + std::string title_name; + uint32_t id; + uint32_t unlocked_achievements_count; + uint32_t achievements_count; + uint32_t title_earned_gamerscore; + uint64_t last_played; // Convert from guest to some tm? +}; + +class GameAchievementsDialog final : public XamDialog { + public: + GameAchievementsDialog(ui::ImGuiDrawer* imgui_drawer, + const ImVec2 drawing_position, + const TitleInfo* title_info, + const UserProfile* profile) + : XamDialog(imgui_drawer), + drawing_position_(drawing_position), + title_info_(*title_info), + profile_(profile) { + LoadAchievementsData(); + } + + private: + bool LoadAchievementsData() { + xe::ui::IconsData data; + + const auto title_achievements = + kernel_state() + ->xam_state() + ->achievement_manager() + ->GetTitleAchievements(profile_->xuid(), title_info_.id); + + const auto title_gpd = kernel_state()->title_xdbf(); + + if (!title_achievements) { + return false; + } + + for (const auto& entry : *title_achievements) { + AchievementInfo info; + info.id = entry.achievement_id; + info.name = + xe::load_and_swap(entry.achievement_name.c_str()); + info.desc = + xe::load_and_swap(entry.unlocked_description.c_str()); + info.unachieved = + xe::load_and_swap(entry.locked_description.c_str()); + + info.flags = entry.flags; + info.gamerscore = entry.gamerscore; + info.image_id = entry.image_id; + info.unlock_time = {}; + + if (entry.IsUnlocked()) { + const uint64_t filetime_time = + (static_cast(entry.unlock_time.high_part) << 32) | + entry.unlock_time.low_part; + + info.unlock_time = chrono::WinSystemClock::to_sys( + chrono::WinSystemClock::from_file_time(filetime_time)); + } + + achievements_info_.insert({info.id, info}); + + const auto& icon_entry = + title_gpd.GetEntry(util::XdbfSection::kImage, info.image_id); + + data.insert({info.image_id, + std::make_pair(icon_entry.buffer, + static_cast(icon_entry.size))}); + } + + achievements_icons_ = imgui_drawer()->LoadIcons(data); + return true; + } + + std::string GetAchievementTitle(const AchievementInfo& achievement_entry) { + std::string title = "Secret trophy"; + + if (achievement_entry.IsUnlocked() || show_locked_info_ || + achievement_entry.flags & + static_cast(AchievementFlags::kShowUnachieved)) { + title = xe::to_utf8(achievement_entry.name); + } + + return title; + } + + std::string GetAchievementDescription( + const AchievementInfo& achievement_entry) { + std::string description = "Hidden description"; + + if (achievement_entry.flags & + static_cast(AchievementFlags::kShowUnachieved)) { + description = xe::to_utf8(achievement_entry.unachieved); + } + + if (achievement_entry.IsUnlocked() || show_locked_info_) { + description = xe::to_utf8(achievement_entry.desc); + } + + return description; + } + + void DrawTitleAchievementInfo(ImGuiIO& io, + const AchievementInfo& achievement_entry) { + const auto start_drawing_pos = ImGui::GetCursorPos(); + + ImGui::TableSetColumnIndex(0); + if (achievement_entry.IsUnlocked() || show_locked_info_) { + if (achievements_icons_.count(achievement_entry.image_id)) { + ImGui::Image(achievements_icons_.at(achievement_entry.image_id).get(), + default_image_icon_size); + } else { + // Case when for whatever reason there is no icon available. + ImGui::Image(0, default_image_icon_size); + } + } else { + ImGui::Image(imgui_drawer()->GetLockedAchievementIcon(), + default_image_icon_size); + } + + ImGui::TableNextColumn(); + + ImGui::PushFont(imgui_drawer()->GetTitleFont()); + const auto primary_line_height = ImGui::GetTextLineHeight(); + ImGui::TextUnformatted( + fmt::format("{}", GetAchievementTitle(achievement_entry)).c_str()); + ImGui::PopFont(); + + ImGui::PushTextWrapPos(ImGui::GetMainViewport()->Size.x * 0.5f); + ImGui::TextWrapped( + fmt::format("{}", GetAchievementDescription(achievement_entry)) + .c_str()); + ImGui::PopTextWrapPos(); + + ImGui::SetCursorPosY(start_drawing_pos.y + default_image_icon_size.x - + ImGui::GetTextLineHeight()); + + if (achievement_entry.IsUnlocked()) { + if (achievement_entry.IsUnlockedOnline()) { + ImGui::TextUnformatted(fmt::format("Unlocked: {:%Y-%m-%d %H:%M}", + achievement_entry.unlock_time) + .c_str()); + } else { + ImGui::TextUnformatted(fmt::format("Unlocked: Locally").c_str()); + } + } + + ImGui::TableNextColumn(); + + // TODO(Gliniak): There is no easy way to align text to middle, so I have to + // do it manually. + const float achievement_row_middle_alignment = + ((default_image_icon_size.x / 2.f) - ImGui::GetTextLineHeight() / 2.f) * + 0.85f; + + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + + achievement_row_middle_alignment); + ImGui::PushFont(imgui_drawer()->GetTitleFont()); + ImGui::TextUnformatted( + fmt::format("{} G", achievement_entry.gamerscore).c_str()); + ImGui::PopFont(); + } + + void OnDraw(ImGuiIO& io) override { + ImGui::SetNextWindowPos(drawing_position_, ImGuiCond_FirstUseEver); + + const auto xenia_window_size = ImGui::GetMainViewport()->Size; + + ImGui::SetNextWindowSizeConstraints( + ImVec2(xenia_window_size.x * 0.2f, xenia_window_size.y * 0.3f), + ImVec2(xenia_window_size.x * 0.6f, xenia_window_size.y * 0.8f)); + ImGui::SetNextWindowBgAlpha(0.8f); + + bool dialog_open = true; + + if (!ImGui::Begin( + fmt::format("{} Achievements List", title_info_.title_name).c_str(), + &dialog_open, + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_HorizontalScrollbar)) { + Close(); + ImGui::End(); + return; + } + + ImGui::Checkbox("Show locked achievements information", &show_locked_info_); + ImGui::Separator(); + + if (achievements_info_.empty()) { + ImGui::TextUnformatted(fmt::format("No achievements data!").c_str()); + } else { + if (ImGui::BeginTable("", 3, + ImGuiTableFlags_::ImGuiTableFlags_BordersInnerH)) { + for (const auto& [_, entry] : achievements_info_) { + ImGui::TableNextRow(0, default_image_icon_size.y); + DrawTitleAchievementInfo(io, entry); + } + + ImGui::EndTable(); + } + } + + if (!dialog_open) { + Close(); + ImGui::End(); + return; + } + + ImGui::End(); + }; + + bool show_locked_info_ = false; + + const ImVec2 drawing_position_ = {}; + + const TitleInfo title_info_; + const UserProfile* profile_; + + std::map achievements_info_; + std::map> achievements_icons_; +}; + +class GamesInfoDialog final : public ui::ImGuiDialog { + public: + GamesInfoDialog(ui::ImGuiDrawer* imgui_drawer, const ImVec2 drawing_position, + const UserProfile* profile) + : ui::ImGuiDialog(imgui_drawer), + drawing_position_(drawing_position), + profile_(profile), + dialog_name_(fmt::format("{}'s Games List", profile->name())) { + LoadProfileGameInfo(imgui_drawer, profile); + } + + private: + void LoadProfileGameInfo(ui::ImGuiDrawer* imgui_drawer, + const UserProfile* profile) { + info_.clear(); + + // TODO(Gliniak): This code should be adjusted for GPD support. Instead of + // using whole profile it should only take vector of gpd entries. Ideally + // remapped to another struct. + if (kernel_state()->emulator()->is_title_open()) { + const auto xdbf = kernel_state()->title_xdbf(); + + if (!xdbf.is_valid()) { + return; + } + + const auto title_summary_info = + kernel_state()->achievement_manager()->GetTitleAchievementsInfo( + profile->xuid(), kernel_state()->title_id()); + + if (!title_summary_info) { + return; + } + + TitleInfo game; + game.id = kernel_state()->title_id(); + game.title_name = xdbf.title(); + game.title_earned_gamerscore = title_summary_info->gamerscore; + game.unlocked_achievements_count = + title_summary_info->unlocked_achievements_count; + game.achievements_count = title_summary_info->achievements_count; + game.last_played = 0; + + xe::ui::IconsData data; + const auto& image_data = xdbf.icon(); + data[game.id] = {image_data.buffer, (uint32_t)image_data.size}; + + title_icon = imgui_drawer->LoadIcons(data); + info_.insert({game.id, game}); + } + } + + void DrawTitleEntry(ImGuiIO& io, const TitleInfo& entry) { + const auto start_position = ImGui::GetCursorPos(); + const ImVec2 next_window_position = + ImVec2(ImGui::GetWindowPos().x + ImGui::GetWindowSize().x + 20.f, + ImGui::GetWindowPos().y); + + // First Column + ImGui::TableSetColumnIndex(0); + ImGui::Image(title_icon.count(entry.id) ? title_icon.at(entry.id).get() : 0, + default_image_icon_size); + + // Second Column + ImGui::TableNextColumn(); + ImGui::PushFont(imgui_drawer()->GetTitleFont()); + ImGui::TextUnformatted(entry.title_name.c_str()); + ImGui::PopFont(); + + ImGui::TextUnformatted( + fmt::format("{}/{} Achievements unlocked ({} Gamerscore)", + entry.unlocked_achievements_count, entry.achievements_count, + entry.title_earned_gamerscore) + .c_str()); + + ImGui::SetCursorPosY(start_position.y + default_image_icon_size.y - + ImGui::GetTextLineHeight()); + + // TODO(Gliniak): For now I left hardcoded now, but in the future it must be + // changed to include last time of boot. + ImGui::TextUnformatted(fmt::format("Last played: {}", "Now").c_str()); + + ImGui::SetCursorPos(start_position); + + if (ImGui::Selectable("##Selectable", false, + ImGuiSelectableFlags_SpanAllColumns, + ImGui::GetContentRegionAvail())) { + new GameAchievementsDialog(imgui_drawer(), next_window_position, &entry, + profile_); + } + } + + void OnDraw(ImGuiIO& io) override { + ImGui::SetNextWindowPos(drawing_position_, ImGuiCond_FirstUseEver); + const auto xenia_window_size = ImGui::GetMainViewport()->Size; + + ImGui::SetNextWindowSizeConstraints( + ImVec2(xenia_window_size.x * 0.05f, xenia_window_size.y * 0.05f), + ImVec2(xenia_window_size.x * 0.4f, xenia_window_size.y * 0.5f)); + ImGui::SetNextWindowBgAlpha(0.8f); + + bool dialog_open = true; + if (!ImGui::Begin(dialog_name_.c_str(), &dialog_open, + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_HorizontalScrollbar)) { + ImGui::End(); + return; + } + + if (!info_.empty()) { + if (ImGui::BeginTable("", 2, + ImGuiTableFlags_::ImGuiTableFlags_BordersInnerH)) { + for (const auto& [_, entry] : info_) { + ImGui::TableNextRow(0, default_image_icon_size.y); + DrawTitleEntry(io, entry); + } + + ImGui::EndTable(); + } + } else { + // Align text to the center + std::string no_entries_message = "There are no titles, so far."; + + ImGui::PushFont(imgui_drawer()->GetTitleFont()); + float windowWidth = ImGui::GetContentRegionAvail().x; + ImVec2 textSize = ImGui::CalcTextSize(no_entries_message.c_str()); + float textOffsetX = (windowWidth - textSize.x) * 0.5f; + if (textOffsetX > 0.0f) { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + textOffsetX); + } + + ImGui::Text(no_entries_message.c_str()); + ImGui::PopFont(); + } + + ImGui::End(); + + if (!dialog_open) { + delete this; + return; + } + } + + std::string dialog_name_ = ""; + const ImVec2 drawing_position_ = {}; + + const UserProfile* profile_; + + std::map> title_icon; + std::map info_; +}; + static dword_result_t XamShowMessageBoxUi( dword_t user_index, lpu16string_t title_ptr, lpu16string_t text_ptr, dword_t button_count, lpdword_t button_ptrs, dword_t active_button, @@ -895,6 +1298,9 @@ bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer, const uint64_t xuid, auto profile_manager = kernel_state()->xam_state()->profile_manager(); const float default_image_size = 75.0f; + const ImVec2 next_window_position = + ImVec2(ImGui::GetWindowPos().x + ImGui::GetWindowSize().x + 20.f, + ImGui::GetWindowPos().y); const ImVec2 drawing_start_position = ImGui::GetCursorPos(); ImVec2 current_drawing_position = ImGui::GetCursorPos(); @@ -970,7 +1376,10 @@ bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer, const uint64_t xuid, } ImGui::EndDisabled(); - ImGui::MenuItem("Show Achievements (unsupported)"); + if (ImGui::MenuItem("Show Achievements")) { + new GamesInfoDialog(imgui_drawer, next_window_position, + profile_manager->GetProfile(user_index)); + } if (ImGui::MenuItem("Show Content Directory")) { const auto path = profile_manager->GetProfileContentPath( @@ -1306,6 +1715,31 @@ dword_result_t XamShowSigninUIp_entry(dword_t user_index, dword_t users_needed, } DECLARE_XAM_EXPORT1(XamShowSigninUIp, kUserProfiles, kImplemented); +dword_result_t XamShowAchievementsUI_entry(dword_t user_index, + dword_t unk_mask) { + auto user = kernel_state()->xam_state()->GetUserProfile(user_index); + if (!user) { + return X_ERROR_NO_SUCH_USER; + } + + if (!kernel_state()->title_xdbf().is_valid()) { + return X_ERROR_FUNCTION_FAILED; + } + + TitleInfo info = {}; + info.id = kernel_state()->title_id(); + info.title_name = kernel_state()->title_xdbf().title(); + + ui::ImGuiDrawer* imgui_drawer = kernel_state()->emulator()->imgui_drawer(); + + auto close = [](GameAchievementsDialog* dialog) -> void {}; + return xeXamDispatchDialogAsync( + new GameAchievementsDialog(imgui_drawer, ImVec2(100.f, 100.f), &info, + user), + close); +} +DECLARE_XAM_EXPORT1(XamShowAchievementsUI, kUserProfiles, kStub); + } // namespace xam } // namespace kernel } // namespace xe diff --git a/src/xenia/ui/imgui_drawer.cc b/src/xenia/ui/imgui_drawer.cc index 6cbbc95b9..8889a0d8e 100644 --- a/src/xenia/ui/imgui_drawer.cc +++ b/src/xenia/ui/imgui_drawer.cc @@ -139,7 +139,11 @@ void ImGuiDrawer::Initialize() { internal_state_ = ImGui::CreateContext(); ImGui::SetCurrentContext(internal_state_); - InitializeFonts(); + const float font_size = std::max((float)cvars::font_size, 8.f); + const float title_font_size = font_size + 6.f; + + InitializeFonts(font_size); + InitializeFonts(title_font_size); auto& style = ImGui::GetStyle(); style.ScrollbarRounding = 6.0f; @@ -230,13 +234,34 @@ std::optional ImGuiDrawer::VirtualKeyToImGuiKey(VirtualKey vkey) { } } +std::map> ImGuiDrawer::LoadIcons( + IconsData data) { + std::map> icons_; + + if (!immediate_drawer_) { + return icons_; + } + + int width, height, channels; + + for (const auto& icon : data) { + unsigned char* image_data = + stbi_load_from_memory(icon.second.first, icon.second.second, &width, + &height, &channels, STBI_rgb_alpha); + + icons_[icon.first] = (immediate_drawer_->CreateTexture( + width, height, ImmediateTextureFilter::kLinear, true, + reinterpret_cast(image_data))); + } + + return icons_; +} + void ImGuiDrawer::SetupNotificationTextures() { if (!immediate_drawer_) { return; } - ImGuiIO& io = GetIO(); - // We're including 4th to include all visible for (uint8_t i = 0; i <= 4; i++) { if (notification_icons.size() < i) { @@ -350,10 +375,9 @@ bool ImGuiDrawer::LoadJapaneseFont(ImGuiIO& io, float font_size) { return false; }; -void ImGuiDrawer::InitializeFonts() { +void ImGuiDrawer::InitializeFonts(const float font_size) { auto& io = ImGui::GetIO(); - const float font_size = std::max((float)cvars::font_size, 8.f); // TODO(gibbed): disable imgui.ini saving for now, // imgui assumes paths are char* so we can't throw a good path at it on // Windows. @@ -423,6 +447,16 @@ void ImGuiDrawer::SetImmediateDrawer(ImmediateDrawer* new_immediate_drawer) { if (immediate_drawer_) { SetupFontTexture(); SetupNotificationTextures(); + + // Load locked achievement icon. + unsigned char* image_data; + int width, height, channels; + image_data = stbi_load_from_memory(locked_achievement_icon.first, + locked_achievement_icon.second, &width, + &height, &channels, STBI_rgb_alpha); + locked_achievement_icon_ = immediate_drawer_->CreateTexture( + width, height, ImmediateTextureFilter::kLinear, true, + reinterpret_cast(image_data)); } } diff --git a/src/xenia/ui/imgui_drawer.h b/src/xenia/ui/imgui_drawer.h index 550a4c515..6ac1e79ce 100644 --- a/src/xenia/ui/imgui_drawer.h +++ b/src/xenia/ui/imgui_drawer.h @@ -34,6 +34,8 @@ class ImGuiDialog; class ImGuiNotification; class Window; +using IconsData = std::map>; + class ImGuiDrawer : public WindowInputListener, public UIDrawer { public: ImGuiDrawer(Window* window, size_t z_order); @@ -60,6 +62,9 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer { void ClearDialogs(); + std::map> LoadIcons( + IconsData data); + ImmediateTexture* GetNotificationIcon(uint8_t user_index) { if (user_index >= notification_icon_textures_.size()) { user_index = 0; @@ -67,6 +72,17 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer { return notification_icon_textures_.at(user_index).get(); } + ImmediateTexture* GetLockedAchievementIcon() { + return locked_achievement_icon_.get(); + } + + ImFont* GetTitleFont() { + if (!GetIO().Fonts->Fonts[1]->IsLoaded()) { + return GetIO().Fonts->Fonts[0]; + } + return GetIO().Fonts->Fonts[1]; + } + protected: void OnKeyDown(KeyEvent& e) override; void OnKeyUp(KeyEvent& e) override; @@ -80,7 +96,7 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer { private: void Initialize(); - void InitializeFonts(); + void InitializeFonts(const float font_size); bool LoadCustomFont(ImGuiIO& io, ImFontConfig& font_config, float font_size); bool LoadWindowsFont(ImGuiIO& io, ImFontConfig& font_config, float font_size); bool LoadJapaneseFont(ImGuiIO& io, float font_size); @@ -123,8 +139,10 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer { // Resources specific to an immediate drawer - must be destroyed before // detaching the presenter. std::unique_ptr font_texture_; + std::unique_ptr locked_achievement_icon_; std::vector> notification_icon_textures_; + // If there's an active pointer, the ImGui mouse is controlled by this touch. // If it's TouchEvent::kPointerIDNone, the ImGui mouse is controlled by the // mouse. diff --git a/src/xenia/ui/resources.h b/src/xenia/ui/resources.h index 993b0582a..11e01320b 100644 --- a/src/xenia/ui/resources.h +++ b/src/xenia/ui/resources.h @@ -4269,6 +4269,338 @@ static const uint8_t player_any_notification_icon[] = { 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82}; static const uint32_t player_any_notification_icon_len = 10320; +static const uint8_t locked_achievement_icon_data[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, + 0x08, 0x06, 0x00, 0x00, 0x00, 0xc3, 0x3e, 0x61, 0xcb, 0x00, 0x00, 0x01, + 0x85, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x28, 0x91, 0x7d, 0x91, 0x3d, 0x48, + 0xc3, 0x40, 0x1c, 0xc5, 0x5f, 0x53, 0xc5, 0x0f, 0x5a, 0x04, 0xed, 0x20, + 0xe2, 0x90, 0xa1, 0x3a, 0x88, 0x5d, 0x54, 0xc4, 0xb1, 0x54, 0xb1, 0x08, + 0x16, 0x4a, 0x5b, 0xa1, 0x55, 0x07, 0x93, 0x4b, 0xbf, 0xa0, 0x49, 0x43, + 0x92, 0xe2, 0xe2, 0x28, 0xb8, 0x16, 0x1c, 0xfc, 0x58, 0xac, 0x3a, 0xb8, + 0x38, 0xeb, 0xea, 0xe0, 0x2a, 0x08, 0x82, 0x1f, 0x20, 0xce, 0x0e, 0x4e, + 0x8a, 0x2e, 0x52, 0xe2, 0xff, 0x92, 0x42, 0x8b, 0x18, 0x0f, 0x8e, 0xfb, + 0xf1, 0xee, 0xde, 0xe3, 0xee, 0x1d, 0x20, 0x34, 0x2a, 0x4c, 0x35, 0xbb, + 0xa2, 0x80, 0xaa, 0x59, 0x46, 0x2a, 0x1e, 0x13, 0xb3, 0xb9, 0x55, 0xb1, + 0xe7, 0x15, 0x01, 0x04, 0x31, 0x88, 0x3e, 0x4c, 0x48, 0xcc, 0xd4, 0x13, + 0xe9, 0xc5, 0x0c, 0x3c, 0xc7, 0xd7, 0x3d, 0x7c, 0x7c, 0xbd, 0x8b, 0xf0, + 0x2c, 0xef, 0x73, 0x7f, 0x8e, 0xa0, 0x92, 0x37, 0x19, 0xe0, 0x13, 0x89, + 0xa3, 0x4c, 0x37, 0x2c, 0xe2, 0x0d, 0xe2, 0xd9, 0x4d, 0x4b, 0xe7, 0xbc, + 0x4f, 0x1c, 0x62, 0x25, 0x49, 0x21, 0x3e, 0x27, 0x9e, 0x34, 0xe8, 0x82, + 0xc4, 0x8f, 0x5c, 0x97, 0x5d, 0x7e, 0xe3, 0x5c, 0x74, 0x58, 0xe0, 0x99, + 0x21, 0x23, 0x93, 0x9a, 0x27, 0x0e, 0x11, 0x8b, 0xc5, 0x0e, 0x96, 0x3b, + 0x98, 0x95, 0x0c, 0x95, 0x78, 0x86, 0x38, 0xac, 0xa8, 0x1a, 0xe5, 0x0b, + 0x59, 0x97, 0x15, 0xce, 0x5b, 0x9c, 0xd5, 0x4a, 0x8d, 0xb5, 0xee, 0xc9, + 0x5f, 0x18, 0xc8, 0x6b, 0x2b, 0x69, 0xae, 0xd3, 0x1c, 0x45, 0x1c, 0x4b, + 0x48, 0x20, 0x09, 0x11, 0x32, 0x6a, 0x28, 0xa3, 0x02, 0x0b, 0x11, 0x5a, + 0x35, 0x52, 0x4c, 0xa4, 0x68, 0x3f, 0xe6, 0xe1, 0x1f, 0x71, 0xfc, 0x49, + 0x72, 0xc9, 0xe4, 0x2a, 0x83, 0x91, 0x63, 0x01, 0x55, 0xa8, 0x90, 0x1c, + 0x3f, 0xf8, 0x1f, 0xfc, 0xee, 0xd6, 0x2c, 0x4c, 0x4f, 0xb9, 0x49, 0x81, + 0x18, 0xd0, 0xfd, 0x62, 0xdb, 0x1f, 0x63, 0x40, 0xcf, 0x2e, 0xd0, 0xac, + 0xdb, 0xf6, 0xf7, 0xb1, 0x6d, 0x37, 0x4f, 0x00, 0xff, 0x33, 0x70, 0xa5, + 0xb5, 0xfd, 0xd5, 0x06, 0x30, 0xf7, 0x49, 0x7a, 0xbd, 0xad, 0x85, 0x8f, + 0x80, 0x81, 0x6d, 0xe0, 0xe2, 0xba, 0xad, 0xc9, 0x7b, 0xc0, 0xe5, 0x0e, + 0x30, 0xfc, 0xa4, 0x4b, 0x86, 0xe4, 0x48, 0x7e, 0x9a, 0x42, 0xa1, 0x00, + 0xbc, 0x9f, 0xd1, 0x37, 0xe5, 0x80, 0xa1, 0x5b, 0xa0, 0x7f, 0xcd, 0xed, + 0xad, 0xb5, 0x8f, 0xd3, 0x07, 0x20, 0x43, 0x5d, 0x2d, 0xdf, 0x00, 0x07, + 0x87, 0xc0, 0x78, 0x91, 0xb2, 0xd7, 0x3d, 0xde, 0xdd, 0xdb, 0xd9, 0xdb, + 0xbf, 0x67, 0x5a, 0xfd, 0xfd, 0x00, 0xa8, 0xaa, 0x72, 0xbc, 0x75, 0x40, + 0x1a, 0xad, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x00, 0x00, 0x00, 0x09, + 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x03, 0x76, 0x00, 0x00, 0x03, 0x76, + 0x01, 0x7d, 0xd5, 0x82, 0xcc, 0x00, 0x00, 0x0d, 0x74, 0x49, 0x44, 0x41, + 0x54, 0x78, 0xda, 0xed, 0x9d, 0xd9, 0x6f, 0x5c, 0xd5, 0x1d, 0xc7, 0x3f, + 0xe7, 0x2e, 0x73, 0x67, 0xb1, 0xc7, 0x5b, 0x62, 0xc7, 0x09, 0x21, 0x01, + 0x13, 0xc2, 0x16, 0x4a, 0x52, 0xa0, 0xa5, 0x94, 0xb8, 0x6a, 0x01, 0x21, + 0xb5, 0x65, 0x69, 0x85, 0x50, 0xa1, 0x54, 0xad, 0x04, 0x4f, 0x95, 0xfa, + 0xd6, 0xff, 0xa1, 0xcf, 0x55, 0x1f, 0xda, 0xe2, 0xb7, 0xd2, 0x48, 0x55, + 0x5b, 0xb5, 0xaa, 0xba, 0x08, 0xac, 0x52, 0x39, 0x2d, 0x65, 0x29, 0xa4, + 0x04, 0xb3, 0x84, 0x52, 0x0c, 0x49, 0x1c, 0x2f, 0x78, 0xf7, 0x78, 0x66, + 0xee, 0x32, 0xf7, 0x9e, 0x3e, 0xdc, 0x63, 0x88, 0x28, 0x71, 0x66, 0xbc, + 0xcc, 0x72, 0xe7, 0x7c, 0xa5, 0x2b, 0x43, 0xe2, 0x68, 0xe6, 0x9c, 0xdf, + 0xf7, 0xf7, 0xfd, 0x2d, 0xe7, 0x9e, 0x73, 0x40, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x43, 0x43, 0x23, 0xe1, 0x10, 0x49, 0x1e, 0xdc, 0x3d, 0x8f, + 0x3e, 0x21, 0x00, 0x13, 0xb0, 0x80, 0x0e, 0xa0, 0x47, 0xfd, 0xcc, 0xa9, + 0x27, 0xa3, 0xfe, 0xde, 0x05, 0x8a, 0x17, 0x3d, 0xcb, 0xc0, 0x2a, 0xe0, + 0x01, 0xe1, 0xe8, 0x89, 0x91, 0x48, 0x13, 0xa0, 0x45, 0x70, 0xef, 0x63, + 0x4f, 0x0a, 0x29, 0xa5, 0x01, 0xa4, 0x80, 0x3c, 0x70, 0x35, 0x70, 0x33, + 0x70, 0x2d, 0xd0, 0x7f, 0x11, 0x01, 0xb2, 0xea, 0x31, 0x14, 0x01, 0x4a, + 0x9f, 0x20, 0xc0, 0xfb, 0xc0, 0x38, 0x70, 0x06, 0x98, 0x53, 0xbf, 0x93, + 0x38, 0x32, 0x88, 0x84, 0x79, 0xbb, 0x05, 0x74, 0x02, 0x07, 0x81, 0xdb, + 0x80, 0x63, 0xc0, 0x35, 0xc0, 0x3e, 0xa0, 0x17, 0x70, 0xd4, 0xef, 0x18, + 0xca, 0xf3, 0x0d, 0x35, 0x07, 0x11, 0x10, 0xaa, 0x27, 0x02, 0x02, 0xa5, + 0x00, 0x33, 0xc0, 0x07, 0x8a, 0x08, 0xff, 0x02, 0xde, 0x02, 0xe6, 0x01, + 0x3f, 0x29, 0x44, 0x10, 0x09, 0x31, 0xbe, 0xa9, 0xbc, 0x7d, 0x08, 0xf8, + 0x22, 0xf0, 0x25, 0xe0, 0x7a, 0xe5, 0xf1, 0x59, 0x65, 0x74, 0x51, 0xe3, + 0x78, 0xa5, 0x22, 0x83, 0x07, 0x2c, 0x02, 0x67, 0x81, 0x7f, 0x02, 0x7f, + 0x03, 0x4e, 0x2b, 0x22, 0x04, 0xa3, 0x27, 0x46, 0xa4, 0x26, 0x40, 0x63, + 0xe5, 0x3e, 0xad, 0x64, 0xfe, 0x4b, 0xc0, 0x30, 0x70, 0x0b, 0xb0, 0x57, + 0x19, 0x7e, 0xbb, 0xc6, 0x27, 0x81, 0x8a, 0x32, 0xfa, 0x19, 0xe0, 0x79, + 0xe0, 0x39, 0xe0, 0x75, 0x60, 0xa9, 0x95, 0xd5, 0xc0, 0x6c, 0x5d, 0xe3, + 0x3f, 0x61, 0x48, 0x49, 0x17, 0x70, 0x27, 0xf0, 0x04, 0xf0, 0x0d, 0x25, + 0xf9, 0x03, 0x4a, 0xea, 0xc5, 0x36, 0x3b, 0x8a, 0xa9, 0xf2, 0x87, 0x41, + 0x95, 0x4f, 0x0c, 0xa9, 0x3f, 0x9f, 0x1f, 0x3a, 0x72, 0xac, 0x38, 0x31, + 0x7e, 0x2a, 0xd2, 0x0a, 0x50, 0x5f, 0xc9, 0xef, 0x57, 0x5e, 0xff, 0x28, + 0x70, 0x07, 0xd0, 0x5d, 0x47, 0x42, 0x4b, 0x95, 0x34, 0xfe, 0x07, 0xf8, + 0x0d, 0xf0, 0x7b, 0x60, 0x02, 0xf0, 0x5a, 0x2d, 0x24, 0x58, 0x2d, 0x68, + 0x7c, 0x0b, 0xd8, 0x0f, 0x7c, 0x15, 0x78, 0x18, 0x38, 0xaa, 0x3c, 0x53, + 0xd4, 0xd9, 0x71, 0x72, 0xc0, 0x4d, 0xea, 0xb3, 0x77, 0x03, 0xbf, 0x56, + 0xb9, 0x41, 0x51, 0x87, 0x80, 0x9d, 0xf5, 0xfc, 0xab, 0x80, 0xc7, 0xd4, + 0x73, 0x44, 0x19, 0x42, 0x34, 0x70, 0xfe, 0xba, 0x14, 0x21, 0x7b, 0x80, + 0x99, 0xa1, 0x23, 0xc7, 0xe6, 0x26, 0xc6, 0x4f, 0x55, 0x34, 0x01, 0xb6, + 0xdf, 0xf8, 0x86, 0x8a, 0xbf, 0x0f, 0x02, 0xdf, 0x56, 0x59, 0x7e, 0xba, + 0x09, 0xc2, 0x98, 0xa1, 0x4a, 0xcf, 0x41, 0xd5, 0x7b, 0x38, 0x37, 0x74, + 0xe4, 0xd8, 0x7c, 0xab, 0xe4, 0x04, 0x66, 0x8b, 0x18, 0x5f, 0x28, 0x0f, + 0xbb, 0x07, 0xf8, 0x8e, 0xf2, 0x7c, 0xa7, 0xc9, 0x72, 0xa9, 0xac, 0x4a, + 0x40, 0x43, 0x45, 0x82, 0xe5, 0x89, 0xf1, 0x53, 0x4d, 0x9f, 0x0f, 0x18, + 0x2d, 0x22, 0x00, 0x19, 0xd5, 0xd8, 0xf9, 0x16, 0xf0, 0x99, 0x6d, 0x32, + 0xbe, 0xfc, 0x94, 0x67, 0xab, 0x73, 0xb9, 0x17, 0xb8, 0x1f, 0xf8, 0x1a, + 0x30, 0xa8, 0x88, 0xab, 0x93, 0xc0, 0x6d, 0x88, 0xfb, 0x87, 0x2e, 0xca, + 0xf6, 0x37, 0x9b, 0xf0, 0x85, 0xaa, 0x96, 0x2f, 0xab, 0x0c, 0xde, 0x57, + 0xff, 0x2f, 0x95, 0x12, 0xda, 0x8a, 0x68, 0x59, 0x25, 0xe5, 0xe6, 0x26, + 0x3e, 0xc7, 0x52, 0xe5, 0xe1, 0x43, 0xc4, 0x8d, 0xa3, 0x67, 0x89, 0x3b, + 0x8a, 0xba, 0x0c, 0xdc, 0x82, 0xf4, 0xef, 0x56, 0x9e, 0xff, 0x03, 0xe2, + 0x16, 0xaf, 0xb1, 0x09, 0xc3, 0xaf, 0x01, 0xb3, 0xc0, 0x39, 0xd5, 0xc8, + 0x39, 0x03, 0x4c, 0x13, 0xf7, 0xfc, 0xc3, 0x8b, 0x32, 0xf9, 0xab, 0x81, + 0x1b, 0xd4, 0xcf, 0xbd, 0x2a, 0xec, 0xd8, 0x9b, 0x98, 0xa7, 0x25, 0x55, + 0x1e, 0xfe, 0x18, 0x78, 0x7b, 0xf4, 0xc4, 0x48, 0xa8, 0x15, 0x60, 0x73, + 0x48, 0xa9, 0xa6, 0xcb, 0x57, 0x80, 0x3d, 0x35, 0x1a, 0x5f, 0x12, 0x2f, + 0xe0, 0x9c, 0x03, 0x5e, 0x06, 0x4e, 0x12, 0x77, 0xee, 0xa6, 0x15, 0x21, + 0xdc, 0x4f, 0x28, 0x80, 0xa3, 0xbc, 0xbf, 0x4f, 0x29, 0xce, 0x17, 0x88, + 0xdb, 0xca, 0xd7, 0xa9, 0x4c, 0xbf, 0x96, 0xcf, 0xee, 0x52, 0xff, 0xf6, + 0x45, 0x60, 0x52, 0x11, 0x4d, 0x2b, 0xc0, 0x26, 0xbc, 0x7f, 0x10, 0x78, + 0x1c, 0xf8, 0x3e, 0xf1, 0x82, 0x4e, 0xb5, 0x46, 0x88, 0x80, 0x02, 0xf1, + 0x02, 0xce, 0xef, 0x88, 0x5b, 0xb7, 0xe7, 0x80, 0xc2, 0xe8, 0x89, 0x91, + 0x0d, 0x4b, 0x34, 0xd5, 0x61, 0xcc, 0xa8, 0x46, 0xd3, 0x67, 0x80, 0x07, + 0x14, 0x01, 0x07, 0x6b, 0x74, 0x98, 0x22, 0xf0, 0x17, 0xe0, 0x47, 0xc0, + 0x6b, 0xcd, 0xaa, 0x02, 0xcd, 0xac, 0x00, 0x36, 0xf1, 0x4a, 0xde, 0x97, + 0x95, 0x3c, 0xd7, 0x62, 0xfc, 0x45, 0x60, 0x0c, 0x78, 0x9a, 0x78, 0x01, + 0x67, 0xf1, 0x72, 0x86, 0x5f, 0xc7, 0xb3, 0xbf, 0x1c, 0x89, 0x80, 0xe2, + 0xbd, 0x8f, 0x3d, 0x79, 0x56, 0x4a, 0x39, 0x07, 0x9c, 0x27, 0x5e, 0x0e, + 0x7e, 0x50, 0x85, 0x20, 0xbb, 0x86, 0xc4, 0xf5, 0x28, 0x70, 0x5c, 0xe5, + 0x03, 0xf3, 0xba, 0x0c, 0xac, 0xcd, 0xfb, 0xbb, 0x94, 0xe7, 0x7d, 0x93, + 0xb8, 0xcd, 0x5b, 0x8d, 0x5a, 0x49, 0x60, 0x85, 0x78, 0xc5, 0x6e, 0x04, + 0xf8, 0xbb, 0x10, 0x62, 0x53, 0x8b, 0x35, 0xef, 0x8d, 0x9f, 0x92, 0x13, + 0xe3, 0xa7, 0xfc, 0xa1, 0x23, 0xc7, 0x16, 0x88, 0x97, 0x84, 0x51, 0x0d, + 0x9f, 0xee, 0x2a, 0xc9, 0x28, 0x54, 0x08, 0x73, 0x81, 0xd7, 0x54, 0x6f, + 0xa0, 0xe9, 0xca, 0xc2, 0x66, 0x2e, 0x03, 0x77, 0x01, 0xb7, 0x2a, 0x22, + 0x54, 0x1b, 0xaa, 0xca, 0xc0, 0x2b, 0xeb, 0x9e, 0x3f, 0x7a, 0x62, 0x64, + 0xf5, 0xd9, 0x5f, 0x3e, 0xb5, 0xa5, 0x49, 0x1f, 0x3d, 0x31, 0xe2, 0x13, + 0xf7, 0xf9, 0x7f, 0xab, 0x24, 0x7d, 0xb6, 0x86, 0x92, 0x31, 0x4d, 0xdc, + 0xb9, 0xbc, 0xba, 0xc9, 0xfa, 0x16, 0x4d, 0x4f, 0x00, 0x53, 0xc5, 0xfc, + 0x9b, 0x6b, 0x98, 0xb8, 0x10, 0x78, 0x57, 0x65, 0xdf, 0xcf, 0x1b, 0x86, + 0x58, 0xdb, 0xae, 0x2f, 0xa3, 0x48, 0xf0, 0x2e, 0xf1, 0xa2, 0xcf, 0x2b, + 0xaa, 0x8c, 0xac, 0x36, 0xc4, 0xee, 0x51, 0x89, 0x6c, 0x87, 0x26, 0x40, + 0xf5, 0xf2, 0x9f, 0x27, 0x5e, 0x68, 0xd9, 0x53, 0x65, 0x98, 0x5a, 0x97, + 0xfe, 0x97, 0x80, 0x93, 0x02, 0x96, 0x9e, 0x79, 0xfa, 0xa9, 0x6d, 0x95, + 0xdb, 0xd1, 0x13, 0x23, 0x65, 0x55, 0x45, 0x3c, 0x07, 0x5c, 0xa8, 0x41, + 0x05, 0xf2, 0xc0, 0x61, 0xa0, 0x4f, 0xb5, 0xb3, 0x35, 0x01, 0xaa, 0x40, + 0x0f, 0xf1, 0x8b, 0x1d, 0xd5, 0x36, 0x7d, 0x42, 0x95, 0x68, 0x3d, 0x0f, + 0x5c, 0x78, 0x76, 0xe7, 0x32, 0xee, 0x15, 0x95, 0x5c, 0x9e, 0x56, 0xe1, + 0xa6, 0xda, 0x64, 0xf0, 0x10, 0x71, 0x9b, 0xd8, 0xd4, 0x04, 0xa8, 0x2e, + 0x79, 0xea, 0x00, 0x0e, 0xa8, 0x18, 0x5a, 0x0d, 0x3c, 0x25, 0xd1, 0x6f, + 0xd4, 0x60, 0x98, 0xcd, 0xa8, 0x40, 0xe5, 0xa2, 0xbe, 0xc2, 0x5c, 0x95, + 0x2a, 0x60, 0xab, 0x92, 0xb2, 0xaf, 0x19, 0xab, 0xae, 0x66, 0x24, 0xc0, + 0xfa, 0xea, 0x5a, 0xb5, 0xcb, 0xbc, 0x52, 0x35, 0x76, 0xde, 0x03, 0xce, + 0x1b, 0x86, 0xb1, 0xd3, 0xf5, 0x76, 0x89, 0xf8, 0x25, 0xd1, 0xa9, 0x1a, + 0xc2, 0x80, 0xa3, 0xaa, 0x07, 0x5b, 0x13, 0xa0, 0xba, 0xc4, 0x29, 0x4f, + 0xf5, 0xaf, 0x75, 0x49, 0xe2, 0xd6, 0xeb, 0x07, 0x40, 0xf1, 0x99, 0xa7, + 0x7f, 0xbe, 0xa3, 0xa5, 0x96, 0x88, 0xdf, 0x18, 0x9e, 0x52, 0x79, 0x80, + 0x5f, 0x43, 0x4f, 0xa3, 0x47, 0x95, 0x85, 0x9a, 0x00, 0x55, 0x10, 0xa0, + 0x43, 0x4d, 0x56, 0xb5, 0x04, 0x58, 0x55, 0x92, 0xbc, 0xf3, 0xdd, 0x36, + 0x21, 0xd6, 0x5f, 0x07, 0x5b, 0x50, 0x35, 0x7e, 0xb5, 0x04, 0xe8, 0xd6, + 0x04, 0xa8, 0x9e, 0x00, 0x9d, 0x35, 0x12, 0xa0, 0xa4, 0x12, 0xb4, 0x1d, + 0x7f, 0x09, 0x43, 0xc4, 0x9f, 0xe7, 0x2b, 0xd2, 0xf9, 0x35, 0x12, 0x40, + 0x87, 0x80, 0x2a, 0x4a, 0x40, 0x93, 0x78, 0x51, 0xa6, 0x96, 0xc9, 0xf2, + 0x14, 0x09, 0x76, 0xbe, 0xd3, 0x16, 0x53, 0x72, 0x7d, 0x59, 0xb9, 0x52, + 0xe5, 0x67, 0x9a, 0x2a, 0xa7, 0xd1, 0x49, 0x60, 0x95, 0x53, 0x5c, 0xeb, + 0x5a, 0xbc, 0x04, 0xc2, 0x3a, 0xae, 0x6c, 0xad, 0x6f, 0x1a, 0x91, 0x35, + 0x8e, 0x09, 0x4d, 0x00, 0x0d, 0x4d, 0x00, 0x0d, 0x4d, 0x00, 0x0d, 0x4d, + 0x00, 0x0d, 0x4d, 0x00, 0x0d, 0x4d, 0x00, 0x0d, 0x4d, 0x00, 0x0d, 0x4d, + 0x00, 0x0d, 0x4d, 0x00, 0x0d, 0x4d, 0x80, 0xad, 0xa2, 0x1e, 0x6f, 0x5c, + 0x86, 0x91, 0x40, 0x26, 0x68, 0xce, 0x9a, 0xaa, 0x37, 0x1d, 0x2a, 0x2b, + 0x1a, 0xa2, 0xf6, 0x0d, 0x0b, 0x42, 0xc0, 0xc1, 0xee, 0x78, 0x31, 0x50, + 0x9e, 0x1c, 0xdc, 0x91, 0xae, 0xb0, 0x38, 0x3e, 0x2d, 0xbf, 0x77, 0xcb, + 0x02, 0xbf, 0x38, 0xdd, 0x23, 0x42, 0xd9, 0x9c, 0x04, 0x6d, 0x69, 0x02, + 0xfc, 0xf4, 0xa1, 0x37, 0xc4, 0x4f, 0xc6, 0x0e, 0x89, 0xf9, 0x92, 0x69, + 0x04, 0x52, 0x08, 0x21, 0xaa, 0x3b, 0xd9, 0xc9, 0x31, 0xa5, 0x79, 0xb8, + 0xcf, 0xcb, 0x3e, 0x7e, 0xd7, 0x3b, 0x1d, 0x3f, 0x7b, 0xc4, 0x09, 0x91, + 0x4b, 0x3b, 0xa3, 0x30, 0x63, 0x29, 0xfc, 0xf0, 0x1f, 0xc2, 0x14, 0x77, + 0xe4, 0xc6, 0x26, 0xba, 0x52, 0x2b, 0x9e, 0x21, 0xa2, 0x2a, 0x8c, 0x1e, + 0x49, 0x10, 0x48, 0xa3, 0x3f, 0x13, 0x59, 0xa3, 0x27, 0x2d, 0x43, 0x1c, + 0xaf, 0x44, 0x9a, 0x00, 0xff, 0x37, 0xb9, 0x8e, 0x15, 0x71, 0xba, 0xe7, + 0x87, 0x77, 0x17, 0xaf, 0x1d, 0x9f, 0x1c, 0x38, 0x30, 0xbf, 0xe6, 0xa4, + 0x0d, 0x03, 0x61, 0x88, 0xcb, 0x79, 0xbe, 0x14, 0xf9, 0x74, 0x65, 0xef, + 0x8d, 0x7b, 0x67, 0xef, 0x1d, 0xcc, 0x4d, 0x5c, 0x87, 0x24, 0xda, 0x39, + 0x5f, 0x13, 0xd8, 0xe6, 0x8a, 0xb8, 0xef, 0xc8, 0xeb, 0xf9, 0xfd, 0xbd, + 0xfb, 0x8f, 0x4c, 0xaf, 0x66, 0xb3, 0x7e, 0x65, 0xe3, 0x6f, 0x18, 0x49, + 0x08, 0x23, 0xac, 0x6c, 0x2a, 0x1c, 0x38, 0xb2, 0x77, 0xe1, 0x7a, 0xa4, + 0x59, 0x96, 0x63, 0xe6, 0x1c, 0xe0, 0x8a, 0x61, 0xaf, 0xe1, 0xa2, 0x20, + 0x9a, 0xc0, 0xf0, 0xeb, 0x7b, 0xeb, 0x0f, 0x03, 0x5f, 0x0c, 0xa3, 0xd4, + 0x6d, 0x6b, 0x5e, 0xc7, 0xd1, 0x72, 0x60, 0x5f, 0x23, 0x44, 0xe4, 0x18, + 0x44, 0x1b, 0x7e, 0x4b, 0x01, 0xd2, 0x32, 0x2b, 0xe5, 0x5c, 0xaa, 0xb4, + 0x64, 0x9b, 0x81, 0x57, 0x8f, 0xef, 0x1c, 0x49, 0xc3, 0x2c, 0x07, 0x99, + 0xbc, 0x1b, 0x38, 0x9d, 0x91, 0x14, 0x1b, 0x3a, 0x91, 0x94, 0x06, 0x91, + 0x34, 0x42, 0xcb, 0x88, 0x3e, 0xcc, 0xa7, 0xd7, 0x4e, 0xa5, 0xac, 0xf2, + 0xbf, 0x81, 0x7f, 0x00, 0xa7, 0x80, 0x45, 0x31, 0xec, 0x85, 0x6d, 0x4b, + 0x00, 0x39, 0x96, 0x31, 0x21, 0xea, 0x03, 0x3e, 0x4f, 0xbc, 0xaf, 0xfe, + 0x38, 0xb0, 0x4f, 0x4a, 0x1c, 0x29, 0x54, 0x2a, 0x50, 0x9d, 0x8f, 0x48, + 0x51, 0xe7, 0xdc, 0x4c, 0x22, 0x05, 0x52, 0x88, 0x6a, 0xbf, 0x9c, 0x40, + 0x46, 0xea, 0x75, 0xb2, 0x25, 0xe0, 0x35, 0xe0, 0x8f, 0xc0, 0x5f, 0x81, + 0xf7, 0xc5, 0xb0, 0xe7, 0xb5, 0x1d, 0x01, 0xe4, 0x58, 0xce, 0x80, 0x4a, + 0x3f, 0xf0, 0x75, 0xe0, 0x11, 0xe2, 0x23, 0xde, 0xf2, 0xb4, 0xf0, 0xd1, + 0x75, 0x35, 0xe4, 0x82, 0x1e, 0xf1, 0x51, 0xb4, 0x7f, 0x00, 0x7e, 0x05, + 0xe2, 0x4d, 0x31, 0xec, 0xfa, 0x8d, 0xf8, 0x32, 0x0d, 0x29, 0x03, 0xe5, + 0x49, 0x47, 0x40, 0x25, 0x07, 0xdc, 0x05, 0x7c, 0x97, 0x78, 0x2b, 0x76, + 0x77, 0x1b, 0x18, 0x7f, 0xdd, 0xe9, 0xd2, 0xc4, 0xbb, 0x85, 0x1e, 0x06, + 0x1e, 0x00, 0x39, 0x28, 0xc7, 0xd2, 0x46, 0xdb, 0x10, 0x00, 0x89, 0x4d, + 0xbc, 0xef, 0xfe, 0x21, 0xe2, 0x1d, 0x40, 0x19, 0x12, 0x7e, 0x72, 0xf9, + 0xa7, 0xc0, 0x04, 0xae, 0x00, 0xee, 0x8b, 0x1d, 0x40, 0x76, 0xb4, 0x05, + 0x01, 0xe4, 0xc9, 0x4e, 0x41, 0xbc, 0x49, 0xe2, 0x6e, 0x15, 0xfb, 0x3b, + 0x68, 0x5f, 0xa4, 0x88, 0x4f, 0x24, 0xb9, 0x1f, 0x18, 0x92, 0x63, 0x8e, + 0x95, 0x7c, 0x05, 0x90, 0x95, 0xf5, 0x83, 0x1e, 0xef, 0xa4, 0xf6, 0x53, + 0x3f, 0x92, 0x88, 0x0e, 0xe0, 0xb3, 0xc0, 0xed, 0x4a, 0x09, 0x93, 0x1e, + 0x02, 0x64, 0x4a, 0x11, 0xe0, 0x2a, 0x9a, 0x74, 0xcb, 0x74, 0x03, 0x72, + 0x82, 0xdd, 0xc4, 0x3b, 0xa1, 0x73, 0xed, 0x90, 0x03, 0xa4, 0x88, 0xf7, + 0xca, 0xe5, 0xb5, 0xf7, 0x7f, 0x84, 0x34, 0xf1, 0x11, 0x34, 0x79, 0x39, + 0xe6, 0x18, 0x49, 0x57, 0x00, 0x4b, 0x65, 0xfc, 0x4d, 0xd0, 0x85, 0xdc, + 0x8e, 0xe3, 0x01, 0xb7, 0x2d, 0x21, 0xcc, 0x12, 0x6f, 0x88, 0xa9, 0xab, + 0x4d, 0xea, 0x6a, 0x04, 0x79, 0xb2, 0x4f, 0x20, 0xd7, 0xd6, 0x4f, 0xe4, + 0x32, 0x1b, 0x69, 0xf6, 0x48, 0x1a, 0x48, 0xa9, 0x56, 0x1a, 0x84, 0xc4, + 0x20, 0x42, 0x88, 0x08, 0xd1, 0xb8, 0x62, 0xc4, 0xa4, 0xfa, 0xdd, 0x50, + 0xad, 0x49, 0x00, 0xe5, 0x6d, 0x82, 0x06, 0x95, 0x7c, 0x91, 0xb4, 0xf0, + 0xc3, 0x34, 0x6b, 0x6e, 0x8e, 0x15, 0x37, 0x8b, 0xeb, 0x3b, 0x44, 0x52, + 0x90, 0xb2, 0x02, 0x3a, 0xd3, 0x65, 0xba, 0x32, 0x45, 0x1c, 0xab, 0x8c, + 0x29, 0x7c, 0xb5, 0x05, 0xb0, 0xee, 0xb9, 0x40, 0xdd, 0xe7, 0xa5, 0x51, + 0x32, 0x5c, 0xd7, 0x81, 0x4a, 0x69, 0xe0, 0x87, 0x39, 0xe6, 0x0b, 0xbb, + 0x78, 0x7f, 0xbe, 0x9f, 0xf3, 0x4b, 0x79, 0x66, 0x0a, 0x36, 0x6b, 0x9e, + 0x89, 0x94, 0x02, 0xc7, 0x8e, 0xd8, 0x95, 0x0d, 0xd8, 0xd7, 0x5d, 0xe6, + 0xaa, 0xbe, 0x05, 0x06, 0x7b, 0x3e, 0x24, 0x97, 0x5a, 0xc6, 0x34, 0x82, + 0xc4, 0x27, 0x1f, 0x56, 0xd2, 0x07, 0x18, 0x46, 0x36, 0x05, 0xb7, 0x97, + 0x73, 0x8b, 0x83, 0xbc, 0x76, 0xbe, 0x9f, 0x57, 0x26, 0xf3, 0x9c, 0x2f, + 0x38, 0xf8, 0xa1, 0x20, 0x92, 0xb1, 0x26, 0x09, 0xc0, 0x36, 0xa0, 0xc7, + 0xa9, 0x70, 0xd3, 0x40, 0x17, 0xb7, 0x1f, 0xe8, 0xe2, 0xd0, 0xc0, 0x34, + 0x7d, 0x1d, 0xb3, 0xa4, 0xac, 0x52, 0xa2, 0x3b, 0x54, 0x89, 0x26, 0x40, + 0x24, 0x2d, 0x16, 0x8a, 0x03, 0x8c, 0x4f, 0x5e, 0xc9, 0xcb, 0x67, 0x77, + 0x73, 0x7a, 0x36, 0xcb, 0xa2, 0xfb, 0xe9, 0x43, 0x0e, 0x43, 0x98, 0x2a, + 0xd9, 0x2c, 0x9c, 0xcb, 0x33, 0x5b, 0x70, 0xb8, 0x75, 0x35, 0xcb, 0xb1, + 0x2b, 0xd3, 0xec, 0xef, 0x9d, 0x24, 0x63, 0x17, 0x34, 0x01, 0x5a, 0x0d, + 0x52, 0x0a, 0xd6, 0xdc, 0x6e, 0xde, 0x9a, 0xda, 0xcf, 0x33, 0x67, 0x06, + 0x79, 0x7b, 0x21, 0x8d, 0x1b, 0x1a, 0x1b, 0x7a, 0xb3, 0x00, 0xfc, 0xd0, + 0x60, 0x7c, 0x21, 0xcd, 0x92, 0xbb, 0x8b, 0xa0, 0x62, 0xe0, 0x58, 0x15, + 0xf6, 0xf5, 0xb8, 0x58, 0x09, 0x0d, 0x07, 0x89, 0xad, 0xc3, 0xfd, 0x30, + 0xcb, 0xd9, 0x85, 0x7d, 0xbc, 0x30, 0xd1, 0xcf, 0xdb, 0x0b, 0x19, 0xbc, + 0xcb, 0x18, 0xff, 0x93, 0xe4, 0xb9, 0x50, 0xb4, 0x78, 0xe9, 0x5c, 0x37, + 0xef, 0xcc, 0xec, 0xa1, 0xe0, 0xf6, 0xa9, 0x8a, 0x41, 0x13, 0xa0, 0x45, + 0xa4, 0xdf, 0x64, 0xbe, 0xd0, 0xcf, 0xe9, 0xc9, 0x7e, 0x5e, 0xff, 0x30, + 0x87, 0xb7, 0x89, 0x8d, 0xe3, 0xa1, 0x14, 0x7c, 0x50, 0x48, 0xf1, 0xf2, + 0xd9, 0x5e, 0xce, 0x2f, 0x0c, 0xe0, 0x87, 0x69, 0x4d, 0x80, 0x56, 0x81, + 0x57, 0xc9, 0x31, 0xb9, 0xdc, 0xc7, 0xe9, 0xe9, 0x4e, 0x96, 0xbd, 0xcd, + 0x0d, 0x31, 0x0e, 0x07, 0x82, 0x37, 0xe7, 0xb2, 0xbc, 0x39, 0xdd, 0xcb, + 0x6a, 0xb9, 0x1b, 0x99, 0xa8, 0xf7, 0x81, 0x13, 0x4a, 0x00, 0x29, 0x05, + 0x5e, 0xd0, 0xc1, 0xdc, 0x6a, 0x9e, 0xc9, 0x55, 0x87, 0x70, 0x0b, 0xd2, + 0x2d, 0x81, 0x55, 0xdf, 0x64, 0x62, 0x21, 0x4f, 0xc1, 0xed, 0xd1, 0x0a, + 0xd0, 0x12, 0x04, 0x40, 0xe0, 0x06, 0x69, 0x16, 0x8b, 0x29, 0x0a, 0xbe, + 0xb9, 0x25, 0x9f, 0x15, 0x40, 0x45, 0x0a, 0x16, 0x4b, 0x0e, 0x05, 0x37, + 0x43, 0x18, 0x99, 0x9a, 0x00, 0xcd, 0x4f, 0x00, 0x03, 0xaf, 0x62, 0x50, + 0xf0, 0x0c, 0x42, 0xb9, 0x1d, 0x8a, 0x02, 0x6e, 0x45, 0x50, 0xf4, 0x0c, + 0x82, 0xd0, 0x04, 0x21, 0x35, 0x01, 0x9a, 0x5f, 0x03, 0xc0, 0x10, 0x62, + 0x7b, 0xfb, 0xfa, 0xe2, 0xa2, 0xb8, 0xa0, 0x09, 0xd0, 0xcc, 0x03, 0x92, + 0xa4, 0xed, 0x90, 0xae, 0x74, 0x88, 0x69, 0x6c, 0xdd, 0x5a, 0x42, 0x40, + 0xda, 0x94, 0x74, 0x3a, 0x21, 0xb6, 0x59, 0x21, 0x69, 0x6f, 0xae, 0x25, + 0x52, 0x01, 0x52, 0x96, 0x4f, 0x3e, 0xe3, 0x93, 0xb5, 0xa2, 0x2d, 0x9b, + 0xcb, 0x14, 0x92, 0xae, 0x8c, 0x4f, 0xd6, 0xf1, 0x30, 0x8d, 0x88, 0xe4, + 0x39, 0x4c, 0xc2, 0x20, 0x84, 0xc4, 0xb1, 0x8a, 0xec, 0xea, 0x58, 0x63, + 0x5f, 0xa7, 0x8f, 0xb9, 0x85, 0x98, 0x2d, 0x81, 0x0e, 0x2b, 0xe2, 0xea, + 0xbe, 0x35, 0x3a, 0xd3, 0x2b, 0xba, 0x0a, 0x68, 0x15, 0xa4, 0xac, 0x35, + 0xf6, 0x74, 0x2d, 0x73, 0x68, 0x57, 0x89, 0x8c, 0x25, 0xb7, 0xe4, 0xfd, + 0xfb, 0xf3, 0x1e, 0x87, 0x07, 0x96, 0xc9, 0xa7, 0x97, 0x1a, 0xf9, 0xae, + 0x80, 0x26, 0x40, 0x4d, 0x83, 0x12, 0x15, 0x7a, 0x3b, 0xe6, 0xb9, 0x61, + 0xcf, 0x22, 0x57, 0x75, 0xbb, 0x9b, 0xca, 0x05, 0x04, 0xd0, 0x9f, 0xad, + 0x70, 0xec, 0x8a, 0x15, 0xae, 0xe8, 0x99, 0x27, 0x65, 0x95, 0x13, 0xa9, + 0x00, 0x89, 0x5c, 0x0c, 0x12, 0x02, 0x72, 0xa9, 0x15, 0xae, 0x1b, 0x9c, + 0x62, 0xa9, 0x94, 0x66, 0xcd, 0xeb, 0xe7, 0x6c, 0x21, 0x45, 0x54, 0x43, + 0x53, 0xa8, 0x3b, 0x1d, 0x72, 0xfb, 0xbe, 0x02, 0xc7, 0xae, 0x9c, 0xa1, + 0xb7, 0x63, 0x8e, 0xcb, 0xef, 0x03, 0xd6, 0x04, 0x68, 0x2a, 0x98, 0x46, + 0x48, 0x5f, 0x6e, 0x96, 0x5b, 0x0f, 0xd8, 0x84, 0x91, 0xc1, 0xd8, 0x44, + 0x1f, 0x67, 0x57, 0x53, 0xb8, 0x15, 0x63, 0xc3, 0x4a, 0xce, 0x32, 0x24, + 0xbb, 0xd3, 0x21, 0x47, 0xf7, 0x16, 0xb8, 0x73, 0x68, 0x8a, 0x03, 0x7d, + 0x17, 0x70, 0xac, 0x62, 0x52, 0xa7, 0x29, 0xd9, 0xef, 0x03, 0x58, 0xa6, + 0xc7, 0x40, 0xd7, 0x14, 0x77, 0x5e, 0x53, 0xa1, 0x2b, 0xe3, 0xf3, 0xf7, + 0xf7, 0xfa, 0x79, 0x75, 0x26, 0x8b, 0x1f, 0x89, 0x4b, 0x84, 0x0e, 0xc9, + 0xc1, 0xce, 0x80, 0x3b, 0x0e, 0x2e, 0x71, 0xdb, 0x81, 0x0b, 0x5c, 0xd9, + 0x37, 0x45, 0x26, 0xb5, 0x4c, 0xa2, 0xe7, 0x88, 0x84, 0xc3, 0x36, 0xcb, + 0xec, 0xee, 0x9c, 0xe4, 0xe8, 0x01, 0x9f, 0x92, 0xef, 0x70, 0x66, 0x21, + 0x83, 0xef, 0x89, 0x4f, 0xcd, 0xf8, 0x6d, 0x03, 0x6e, 0xe8, 0x2f, 0xf1, + 0xb9, 0x83, 0x53, 0x1c, 0xdc, 0x35, 0x91, 0xd8, 0xb8, 0x9f, 0xf8, 0x24, + 0xf0, 0xff, 0x06, 0x69, 0x54, 0x70, 0xac, 0x35, 0x1c, 0x2b, 0x60, 0xa3, + 0x00, 0x20, 0x00, 0xcb, 0x88, 0x48, 0xdb, 0x1e, 0x96, 0xe9, 0xd2, 0x16, + 0x73, 0xd3, 0x16, 0xa3, 0x44, 0xe0, 0x06, 0x0e, 0x2b, 0xae, 0x49, 0x10, + 0x5d, 0x3a, 0x11, 0x0c, 0x25, 0xac, 0x78, 0x06, 0xe5, 0xc0, 0x42, 0x4a, + 0x53, 0x13, 0x20, 0x29, 0x90, 0x08, 0xdc, 0x4a, 0x86, 0xe5, 0xb2, 0x4d, + 0x70, 0x89, 0x4a, 0x40, 0x28, 0x02, 0x2c, 0xb9, 0x26, 0xe5, 0xc0, 0x26, + 0xd2, 0x04, 0x48, 0x10, 0x01, 0xa4, 0xc0, 0xab, 0x64, 0x58, 0x75, 0x6d, + 0xc2, 0x0d, 0xaa, 0xb9, 0x48, 0xc2, 0x8a, 0x6b, 0xe1, 0x05, 0x69, 0xa4, + 0xb4, 0x35, 0x01, 0x92, 0xe2, 0xff, 0x20, 0x08, 0x82, 0x0c, 0x45, 0x2f, + 0x45, 0x24, 0x37, 0x56, 0x8a, 0x35, 0xdf, 0xc6, 0x0b, 0x1c, 0x22, 0x4d, + 0x80, 0xe4, 0x20, 0x8a, 0x4c, 0xdc, 0x20, 0x45, 0x29, 0xb0, 0xd8, 0x68, + 0x79, 0x48, 0x4a, 0xd4, 0xbb, 0x04, 0x36, 0x7e, 0x68, 0x25, 0xf0, 0x05, + 0xb0, 0x36, 0x24, 0x80, 0x44, 0x10, 0x84, 0x36, 0x05, 0xcf, 0xa2, 0x18, + 0x18, 0xc8, 0x0d, 0xac, 0x2a, 0x04, 0x04, 0x11, 0x2c, 0x96, 0x0c, 0xca, + 0xbe, 0x4d, 0x3b, 0x30, 0x20, 0xf9, 0x0a, 0x20, 0xc1, 0x0f, 0x1d, 0x0a, + 0xae, 0x49, 0x31, 0xe0, 0xb2, 0xcb, 0xf9, 0x81, 0x14, 0x2c, 0x14, 0x2d, + 0xca, 0x41, 0xb5, 0xf7, 0x56, 0x6a, 0x02, 0x34, 0x7d, 0x09, 0x18, 0x84, + 0x69, 0x8a, 0x9e, 0x4d, 0xe9, 0x32, 0x0a, 0x00, 0x10, 0x46, 0xb0, 0x54, + 0xb6, 0xf1, 0x82, 0x74, 0x2c, 0x09, 0x9a, 0x00, 0x2d, 0x2e, 0x00, 0x02, + 0xa2, 0xd0, 0xc1, 0x0d, 0xec, 0xcb, 0xee, 0x0f, 0x10, 0xaa, 0x12, 0x58, + 0xf5, 0x52, 0x04, 0x61, 0xba, 0x2d, 0x72, 0x00, 0xab, 0x0d, 0xc6, 0xa8, + 0xea, 0x00, 0xf1, 0xd1, 0x7f, 0x6f, 0x48, 0x82, 0xf5, 0x77, 0x09, 0xa5, + 0xf8, 0x78, 0xe7, 0xa8, 0x26, 0x40, 0x0b, 0x07, 0x00, 0x09, 0xe9, 0x54, + 0x91, 0xc1, 0xee, 0x02, 0x87, 0x7b, 0x73, 0x5c, 0x28, 0xa4, 0x89, 0x2e, + 0xd1, 0x0d, 0x14, 0x02, 0x3a, 0x53, 0x15, 0x6e, 0x18, 0x58, 0x21, 0x9f, + 0x5d, 0x69, 0xc4, 0x19, 0x01, 0x9a, 0x00, 0xdb, 0x4e, 0x00, 0x21, 0xe9, + 0x70, 0x96, 0xb8, 0x71, 0xdf, 0x39, 0x1c, 0x3b, 0x62, 0x7e, 0xb5, 0x8b, + 0x28, 0x32, 0x2e, 0xf9, 0xbb, 0xb9, 0x74, 0x89, 0x83, 0xbb, 0x66, 0xd9, + 0xdd, 0x39, 0x8b, 0x40, 0x13, 0x60, 0x27, 0x55, 0xb9, 0x7e, 0x83, 0x34, + 0x02, 0xfa, 0x3b, 0xa7, 0xe9, 0xc9, 0xae, 0x10, 0x45, 0x1b, 0x9f, 0xc2, + 0x22, 0x44, 0x80, 0x65, 0xba, 0x98, 0xc2, 0xa7, 0x1d, 0xd0, 0x88, 0x23, + 0x62, 0x22, 0xe2, 0xbb, 0x21, 0xea, 0x4a, 0x02, 0x43, 0x84, 0x38, 0xd6, + 0x6a, 0x95, 0x1f, 0xdb, 0x90, 0xd3, 0x5a, 0x1a, 0x33, 0x2f, 0x75, 0x95, + 0xe3, 0xe3, 0x4b, 0x92, 0xf8, 0xc6, 0xed, 0x35, 0xf5, 0xb3, 0xee, 0x25, + 0x61, 0x3c, 0xe4, 0xcb, 0x3d, 0x75, 0x37, 0xfe, 0xfa, 0x95, 0xf4, 0xa5, + 0x44, 0x13, 0x40, 0xc1, 0x07, 0xe6, 0x01, 0x97, 0xb6, 0xe8, 0xb5, 0x55, + 0x85, 0x00, 0x58, 0x01, 0x56, 0xeb, 0x7d, 0x7f, 0x80, 0xd1, 0xa0, 0xc1, + 0x4e, 0x03, 0x73, 0x8d, 0x51, 0x81, 0xa6, 0x44, 0x01, 0x78, 0x4f, 0x29, + 0x23, 0x09, 0x27, 0x80, 0x11, 0x00, 0xe7, 0x81, 0xd7, 0xd5, 0xc0, 0xdb, + 0x5d, 0x05, 0x2a, 0xc4, 0x77, 0x07, 0xbc, 0x0c, 0x14, 0x13, 0x4f, 0x00, + 0x31, 0x5c, 0x0e, 0x81, 0x0b, 0xc4, 0xb7, 0x65, 0xfc, 0x57, 0x29, 0x42, + 0xbb, 0x22, 0x52, 0x6a, 0xf8, 0x1c, 0xf1, 0x2d, 0x22, 0x5e, 0x1b, 0x28, + 0x00, 0x80, 0x28, 0x02, 0x2f, 0x00, 0x7f, 0x01, 0x66, 0xd5, 0x44, 0xb4, + 0xab, 0xf4, 0xbf, 0x00, 0xfc, 0x09, 0xc4, 0x87, 0x8d, 0xb8, 0x44, 0xaa, + 0x21, 0x04, 0x10, 0xc3, 0x6e, 0x04, 0xcc, 0x10, 0x5f, 0x99, 0x32, 0xaa, + 0xbc, 0xa0, 0xd2, 0x66, 0x9e, 0xbf, 0x0a, 0xbc, 0x0a, 0xfc, 0x0e, 0x78, + 0x5b, 0x85, 0xc6, 0xa4, 0xf7, 0x01, 0x2e, 0x86, 0x1d, 0x40, 0x70, 0x06, + 0x18, 0x21, 0xbe, 0x48, 0xe9, 0x6e, 0x60, 0x88, 0xf8, 0xd0, 0xe4, 0xa4, + 0x2e, 0x52, 0x49, 0x15, 0xf2, 0x66, 0x80, 0x17, 0x81, 0xdf, 0x03, 0x7f, + 0x8b, 0xb3, 0xff, 0x52, 0x43, 0x72, 0xa1, 0x66, 0xb8, 0x36, 0x6e, 0xfd, + 0xfe, 0x80, 0xbb, 0x81, 0x7b, 0x88, 0xaf, 0x92, 0xd9, 0x4d, 0x7c, 0x91, + 0x42, 0x8a, 0x64, 0x2c, 0xc7, 0x54, 0x80, 0xb2, 0x22, 0xfa, 0x59, 0xe2, + 0x6b, 0xe3, 0xfe, 0x0c, 0x8c, 0x83, 0x51, 0x14, 0xc3, 0xe5, 0x86, 0x25, + 0xc2, 0x4d, 0x31, 0xb9, 0xea, 0x8c, 0xfc, 0x2e, 0xe0, 0x7a, 0xe2, 0x3b, + 0x84, 0x86, 0x88, 0xef, 0x14, 0xc8, 0xd1, 0xfa, 0x17, 0x49, 0xad, 0xdf, + 0x12, 0xb6, 0x6e, 0xfc, 0x37, 0x81, 0x37, 0x80, 0x19, 0x31, 0xec, 0x35, + 0xbc, 0xdf, 0xdc, 0x34, 0xde, 0xa5, 0x2e, 0x90, 0x34, 0x89, 0xaf, 0x4d, + 0xc9, 0x11, 0x1f, 0x29, 0x6f, 0x25, 0x44, 0x01, 0x42, 0x3e, 0xee, 0xf4, + 0x95, 0x80, 0x40, 0x0c, 0x7b, 0xed, 0x9a, 0xf8, 0x6a, 0x68, 0x68, 0x68, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, + 0x68, 0x68, 0x68, 0x68, 0x68, 0xec, 0x24, 0xfe, 0x07, 0x09, 0xfe, 0x9c, + 0xfd, 0xaa, 0x0b, 0xa9, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82}; +static const uint32_t locked_achievement_icon_len = 3941; + static const std::vector> notification_icons = { {player_one_notification_icon, player_one_notification_icon_len}, @@ -4277,3 +4609,6 @@ static const std::vector> {player_four_notification_icon, player_four_notification_icon_len}, {player_any_notification_icon, player_any_notification_icon_len}, }; + +static const std::pair locked_achievement_icon = { + locked_achievement_icon_data, locked_achievement_icon_len};