fix: UB due to assuming wchar_t is utf16

This commit is contained in:
Tropical 2026-03-12 03:11:49 -05:00
parent 6372145a41
commit b20c17c4b7
12 changed files with 49 additions and 28 deletions

View file

@ -1203,6 +1203,8 @@ void UIController::setupCustomDrawGameState()
RenderManager.StartFrame();
#elif defined __PSVITA__
RenderManager.StartFrame();
#elif defined __linux__
RenderManager.StartFrame();
#elif defined __ORBIS__
RenderManager.StartFrame(false);
// Set up a viewport for the render that matches Iggy's own viewport, apart form using an opengl-style z-range (Iggy uses a DX-style range on PS4), so

View file

@ -283,7 +283,7 @@ void UIScene::loadMovie()
#elif defined __PSVITA__
moviePath.append(L"Vita.swf");
m_loadedResolution = eSceneResolution_Vita;
#elif defined _WINDOWS64 || defined __linux__
#elif defined _WINDOWS64
if(ui.getScreenHeight() == 720)
{
moviePath.append(L"720.swf");
@ -994,8 +994,7 @@ bool UIScene::allowRepeat(int key)
void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
{
if(wcscmp((wchar_t *)call->function_name.string,L"handlePress")==0)
{
if(std::char_traits<char16_t>::compare(call->function_name.string, u"handlePress", 12) == 0) {
if(call->num_arguments != 2)
{
app.DebugPrintf("Callback for handlePress did not have the correct number of arguments\n");
@ -1014,7 +1013,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
}
handlePress(call->arguments[0].number, call->arguments[1].number);
}
else if(wcscmp((wchar_t *)call->function_name.string,L"handleFocusChange")==0)
else if(std::char_traits<char16_t>::compare(call->function_name.string, u"handleFocusChange", 18) == 0)
{
if(call->num_arguments != 2)
{
@ -1034,7 +1033,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
}
_handleFocusChange(call->arguments[0].number, call->arguments[1].number);
}
else if(wcscmp((wchar_t *)call->function_name.string,L"handleInitFocus")==0)
else if(std::char_traits<char16_t>::compare(call->function_name.string, u"handleInitFocus", 16) == 0)
{
if(call->num_arguments != 2)
{
@ -1054,7 +1053,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
}
_handleInitFocus(call->arguments[0].number, call->arguments[1].number);
}
else if(wcscmp((wchar_t *)call->function_name.string,L"handleCheckboxToggled")==0)
else if(std::char_traits<char16_t>::compare(call->function_name.string, u"handleCheckboxToggled", 22) == 0)
{
if(call->num_arguments != 2)
{
@ -1074,7 +1073,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
}
handleCheckboxToggled(call->arguments[0].number, call->arguments[1].boolval);
}
else if(wcscmp((wchar_t *)call->function_name.string,L"handleSliderMove")==0)
else if(std::char_traits<char16_t>::compare(call->function_name.string, u"handleSliderMove", 17) == 0)
{
if(call->num_arguments != 2)
{
@ -1094,7 +1093,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
}
handleSliderMove(call->arguments[0].number, call->arguments[1].number);
}
else if(wcscmp((wchar_t *)call->function_name.string,L"handleAnimationEnd")==0)
else if(std::char_traits<char16_t>::compare(call->function_name.string, u"handleAnimationEnd", 19) == 0)
{
if(call->num_arguments != 0)
{
@ -1106,7 +1105,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
}
handleAnimationEnd();
}
else if(wcscmp((wchar_t *)call->function_name.string,L"handleSelectionChanged")==0)
else if(std::char_traits<char16_t>::compare(call->function_name.string, u"handleSelectionChanged", 23) == 0)
{
if(call->num_arguments != 1)
{
@ -1126,7 +1125,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
}
handleSelectionChanged(call->arguments[0].number);
}
else if(wcscmp((wchar_t *)call->function_name.string,L"handleRequestMoreData")==0)
else if(std::char_traits<char16_t>::compare(call->function_name.string, u"handleRequestMoreData", 22) == 0)
{
if(call->num_arguments == 0)
{
@ -1153,7 +1152,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
handleRequestMoreData(call->arguments[0].number, call->arguments[1].boolval);
}
}
else if(wcscmp((wchar_t *)call->function_name.string,L"handleTouchBoxRebuild")==0)
else if(std::char_traits<char16_t>::compare(call->function_name.string, u"handleTouchBoxRebuild", 22) == 0)
{
handleTouchBoxRebuild();
}
@ -1248,3 +1247,25 @@ bool UIScene::isReadyToDelete()
{
return true;
}
static int UIScene::parseSlotId(const char16_t *s) {
// must be nonnull, must start with 'slot_', first char after the underscore must be a digit
if (!s ||
(s[0] != u's' || s[1] != u'l' || s[2] != u'o' || s[3] != u't' ||
s[4] != u'_') ||
(s[5] < u'0' || s[5] > u'9')) {
return -1;
}
int i = 5;
int id = 0;
// keep consuming digits until we reach a non-digit. each digit scales the existing id value
// by 10 plus the actual digit value. (this is referred to as a 'number' by the way)
while (s[i] >= u'0' && s[i] <= u'9') {
id = id * 10 + (s[i] - u'0');
i++;
}
return id;
}

View file

@ -267,4 +267,6 @@ protected:
size_t GetCallbackUniqueId();
virtual bool isReadyToDelete();
static int parseSlotId(const char16_t *s);
};

View file

@ -222,15 +222,14 @@ void UIScene_AbstractContainerMenu::customDraw(IggyCustomDrawCallbackRegion *reg
if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return;
std::shared_ptr<ItemInstance> item = nullptr;
if(wcscmp((wchar_t *)region->name,L"pointerIcon")==0)
if(std::char_traits<char16_t>::compare(region->name, u"pointerIcon", 11) == 0)
{
m_cacheSlotRenders = false;
item = pMinecraft->localplayers[m_iPad]->inventory->getCarried();
}
else
{
int slotId = -1;
swscanf((wchar_t*)region->name,L"slot_%d",&slotId);
int slotId = parseSlotId(region->name);
if (slotId == -1)
{
app.DebugPrintf("This is not the control we are looking for\n");

View file

@ -436,11 +436,10 @@ void UIScene_CraftingMenu::customDraw(IggyCustomDrawCallbackRegion *region)
if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return;
std::shared_ptr<ItemInstance> item = nullptr;
int slotId = -1;
float alpha = 1.0f;
bool decorations = true;
bool inventoryItem = false;
swscanf((wchar_t*)region->name,L"slot_%d",&slotId);
int slotId = parseSlotId(region->name);
if (slotId == -1)
{
app.DebugPrintf("This is not the control we are looking for\n");

View file

@ -120,6 +120,7 @@ void UIScene_DebugOverlay::customDraw(IggyCustomDrawCallbackRegion *region)
if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return;
int itemId = -1;
// 4jcraft: TODO: UB on our platform since this casts char16_t* to wchar_t*
swscanf((wchar_t*)region->name,L"item_%d",&itemId);
if (itemId == -1 || itemId > Item::ITEM_NUM_COUNT || Item::items[itemId] == NULL)
{

View file

@ -250,7 +250,7 @@ void UIScene_EnchantingMenu::customDraw(IggyCustomDrawCallbackRegion *region)
if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return;
if(wcscmp((wchar_t *)region->name,L"EnchantmentBook")==0)
if(std::char_traits<char16_t>::compare(region->name, u"EnchantmentBook", 15) == 0)
{
// Setup GDraw, normal game render states and matrices
CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region);
@ -263,8 +263,7 @@ void UIScene_EnchantingMenu::customDraw(IggyCustomDrawCallbackRegion *region)
}
else
{
int slotId = -1;
swscanf((wchar_t*)region->name,L"slot_Button%d",&slotId);
int slotId = parseSlotId(region->name);
if(slotId >= 0)
{
// Setup GDraw, normal game render states and matrices

View file

@ -182,8 +182,7 @@ void UIScene_HUD::customDraw(IggyCustomDrawCallbackRegion *region)
Minecraft *pMinecraft = Minecraft::GetInstance();
if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return;
int slot = -1;
swscanf((wchar_t*)region->name,L"slot_%d",&slot);
int slot = parseSlotId(region->name);
if (slot == -1)
{
app.DebugPrintf("This is not the control we are looking for\n");

View file

@ -222,7 +222,7 @@ void UIScene_InventoryMenu::customDraw(IggyCustomDrawCallbackRegion *region)
Minecraft *pMinecraft = Minecraft::GetInstance();
if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return;
if(wcscmp((wchar_t *)region->name,L"player")==0)
if(std::char_traits<char16_t>::compare(region->name, u"player", 6) == 0)
{
// Setup GDraw, normal game render states and matrices
CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region);

View file

@ -961,11 +961,11 @@ int UIScene_LeaderboardsMenu::SetLeaderboardTitleIcons()
void UIScene_LeaderboardsMenu::customDraw(IggyCustomDrawCallbackRegion *region)
{
int slotId = -1;
swscanf((wchar_t*)region->name,L"slot_%d",&slotId);
int slotId = parseSlotId(region->name);
if (slotId == -1)
{
//app.DebugPrintf("This is not the control we are looking for\n");
//assert(0);
app.DebugPrintf("This is not the control we are looking for\n");
}
else
{

View file

@ -424,7 +424,7 @@ void UIScene_MainMenu::RunAction(int iPad)
void UIScene_MainMenu::customDraw(IggyCustomDrawCallbackRegion *region)
{
if(wcscmp((wchar_t *)region->name,L"Splash")==0)
if(std::char_traits<char16_t>::compare(region->name, u"Splash", 6) == 0)
{
PIXBeginNamedEvent(0,"Custom draw splash");
customDrawSplash(region);

View file

@ -148,8 +148,7 @@ void UIScene_TradingMenu::customDraw(IggyCustomDrawCallbackRegion *region)
if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return;
std::shared_ptr<ItemInstance> item = nullptr;
int slotId = -1;
swscanf((wchar_t*)region->name,L"slot_%d",&slotId);
int slotId = parseSlotId(region->name);
if(slotId < MerchantMenu::USE_ROW_SLOT_END)
{