#include "MenuManager.h" #include "PLBigEndian.h" #include "MemoryManager.h" #include struct MenuItem { uint32_t nameOffsetInStringBlob; int8_t iconResID; uint8_t key; uint8_t submenuID; uint8_t textStyle; bool enabled; bool checked; }; struct Menu { uint16_t menuID; uint16_t width; uint16_t height; uint16_t commandID; bool enabled; PortabilityLayer::MMHandleBlock *stringBlobHandle; Menu **prevMenu; Menu **nextMenu; size_t numMenuItems; MenuItem menuItems[1]; }; namespace PortabilityLayer { class MenuManagerImpl final : public MenuManager { public: MenuManagerImpl(); ~MenuManagerImpl(); Menu **DeserializeMenu(const void *resData) const override; Menu **GetMenuByID(int id) const override; void InsertMenuBefore(Menu **insertingMenu, Menu **existingMenu) override; void InsertMenuAfter(Menu **insertingMenu, Menu **existingMenu) override; void InsertMenuAtEnd(Menu **insertingMenu) override; void InsertMenuAtBeginning(Menu **insertingMenu) override; void SetMenuEnabled(Menu **menuHandle, bool enabled) override; void SetItemEnabled(Menu **menu, unsigned int index, bool enabled) override; void SetItemChecked(Menu **menu, unsigned int index, bool checked) override; static MenuManagerImpl *GetInstance(); private: Menu **m_firstMenu; Menu **m_lastMenu; static MenuManagerImpl ms_instance; }; MenuManagerImpl::MenuManagerImpl() : m_firstMenu(nullptr) , m_lastMenu(nullptr) { } MenuManagerImpl::~MenuManagerImpl() { } Menu **MenuManagerImpl::DeserializeMenu(const void *resData) const { PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance(); const uint8_t *resBytes = static_cast(resData); struct MenuDataSerialized { BEUInt16_t menuID; BEUInt16_t width; BEUInt16_t height; BEUInt16_t commandID; BEUInt16_t padding; BEUInt32_t enableFlags; }; MenuDataSerialized header; memcpy(&header, resBytes, sizeof(header)); const uint8_t titleLength = resBytes[14]; const uint8_t *menuDataStart = resBytes + 14 + 1 + titleLength; size_t stringDataLength = 1 + titleLength; size_t numMenuItems = 0; for (const uint8_t *menuItemStart = menuDataStart; *menuItemStart; menuItemStart += 5 + (*menuItemStart)) { numMenuItems++; stringDataLength += 1 + (*menuItemStart); } MMHandleBlock *stringData = mm->AllocHandle(stringDataLength); if (!stringData) { mm->ReleaseHandle(stringData); return nullptr; } MMHandleBlock *menuData = mm->AllocHandle(sizeof(Menu) + sizeof(MenuItem) * (numMenuItems - 1)); if (!menuData) { mm->ReleaseHandle(stringData); return nullptr; } uint32_t enableFlags = header.enableFlags; Menu *menu = static_cast(menuData->m_contents); menu->menuID = header.menuID; menu->width = header.width; menu->height = header.height; menu->commandID = header.commandID; menu->enabled = ((enableFlags & 1) != 0); uint8_t *stringDataStart = static_cast(stringData->m_contents); uint8_t *stringDest = stringDataStart; memcpy(stringDest, resBytes + 14, 1 + resBytes[14]); stringDest += 1 + resBytes[14]; enableFlags >>= 1; MenuItem *currentItem = menu->menuItems; for (const uint8_t *menuItemStart = menuDataStart; *menuItemStart; menuItemStart += 5 + (*menuItemStart)) { uint8_t itemNameLength = *menuItemStart; memcpy(stringDest, menuItemStart, 1 + itemNameLength); currentItem->iconResID = static_cast(menuItemStart[1 + itemNameLength]); currentItem->key = menuItemStart[2 + itemNameLength]; currentItem->submenuID = menuItemStart[3 + itemNameLength]; currentItem->textStyle = menuItemStart[4 + itemNameLength]; currentItem->nameOffsetInStringBlob = static_cast(stringDest - stringDataStart); currentItem->enabled = ((enableFlags & 1) != 0); currentItem->checked = false; enableFlags >>= 1; currentItem++; stringDest += 1 + (*menuItemStart); } menu->numMenuItems = numMenuItems; menu->stringBlobHandle = stringData; menu->prevMenu = nullptr; menu->nextMenu = nullptr; return reinterpret_cast(&menuData->m_contents); } Menu **MenuManagerImpl::GetMenuByID(int id) const { for (Menu **menuHandle = m_firstMenu; menuHandle; menuHandle = (*menuHandle)->nextMenu) { if ((*menuHandle)->menuID == id) return menuHandle; } return nullptr; } void MenuManagerImpl::InsertMenuBefore(Menu **insertingMenu, Menu **existingMenu) { Menu *insertingMenuPtr = *insertingMenu; Menu *existingMenuPtr = *existingMenu; insertingMenuPtr->prevMenu = existingMenuPtr->prevMenu; insertingMenuPtr->nextMenu = existingMenu; if (existingMenuPtr->prevMenu) (*existingMenuPtr->prevMenu)->nextMenu = insertingMenu; else m_firstMenu = insertingMenu; existingMenuPtr->prevMenu = insertingMenu; } void MenuManagerImpl::InsertMenuAfter(Menu **insertingMenu, Menu **existingMenu) { Menu *insertingMenuPtr = *insertingMenu; Menu *existingMenuPtr = *existingMenu; insertingMenuPtr->prevMenu = existingMenu; insertingMenuPtr->nextMenu = existingMenuPtr->nextMenu; if (existingMenuPtr->nextMenu) (*existingMenuPtr->nextMenu)->prevMenu = insertingMenu; else m_lastMenu = insertingMenu; existingMenuPtr->nextMenu = insertingMenu; } void MenuManagerImpl::InsertMenuAtEnd(Menu **insertingMenu) { if (m_firstMenu == nullptr) { m_firstMenu = m_lastMenu = insertingMenu; return; } (*m_lastMenu)->nextMenu = insertingMenu; (*insertingMenu)->prevMenu = m_lastMenu; m_lastMenu = insertingMenu; } void MenuManagerImpl::InsertMenuAtBeginning(Menu **insertingMenu) { if (m_firstMenu == nullptr) { m_firstMenu = m_lastMenu = insertingMenu; return; } (*m_firstMenu)->prevMenu = insertingMenu; (*insertingMenu)->nextMenu = m_firstMenu; m_firstMenu = insertingMenu; } void MenuManagerImpl::SetMenuEnabled(Menu **menuHandle, bool enabled) { Menu *menu = *menuHandle; menu->enabled = enabled; } void MenuManagerImpl::SetItemEnabled(Menu **menuHandle, unsigned int index, bool enabled) { Menu *menu = *menuHandle; if (index >= menu->numMenuItems) return; menu->menuItems[index].enabled = enabled; } void MenuManagerImpl::SetItemChecked(Menu **menuHandle, unsigned int index, bool checked) { Menu *menu = *menuHandle; if (index >= menu->numMenuItems) return; menu->menuItems[index].checked = checked; } MenuManagerImpl *MenuManagerImpl::GetInstance() { return &ms_instance; } MenuManagerImpl MenuManagerImpl::ms_instance; MenuManager *MenuManager::GetInstance() { return MenuManagerImpl::GetInstance(); } }