From 72af9e00fba7f0f99a485c1dbcba0c22e714b644 Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 2 Mar 2026 18:21:42 -0300 Subject: [PATCH] Working CustomSkin selector --- .gitignore | 1 + Minecraft.Client/Common/Consoles_App.cpp | 107 +++++++++++++++--- Minecraft.Client/Common/Consoles_App.h | 3 + .../Common/UI/UIScene_SkinSelectMenu.cpp | 62 +++++++++- .../Common/XUI/XUI_SkinSelect.cpp | 86 ++++++++++++-- 5 files changed, 235 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index ac97f2f..ba39cb7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ ipch/ # =========================================== # Visual Studio files # =========================================== +.vs/ *.sdf *.opensdf *.suo diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index cf855ca..d21d5cb 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -153,10 +153,17 @@ CMinecraftApp::CMinecraftApp() m_xuidNotch = INVALID_XUID; - ZeroMemory(&m_InviteData,sizeof(JoinFromInviteData) ); + InitializeCriticalSection(&csDLCDownloadQueue); + InitializeCriticalSection(&csTMSPPDownloadQueue); + InitializeCriticalSection(&csAdditionalModelParts); + InitializeCriticalSection(&csAdditionalSkinBoxes); + InitializeCriticalSection(&csAnimOverrideBitmask); + InitializeCriticalSection(&csMemFilesLock); + InitializeCriticalSection(&csMemTPDLock); -// m_bRead_TMS_XUIDS_XML=false; -// m_bRead_TMS_DLCINFO_XML=false; + ScanAndLoadCustomSkins(); + + ZeroMemory(&m_InviteData,sizeof(JoinFromInviteData) ); m_pDLCFileBuffer=NULL; m_dwDLCFileSize=0; @@ -172,7 +179,6 @@ CMinecraftApp::CMinecraftApp() m_uiAutosaveTimer=0; ZeroMemory(m_pszUniqueMapName,14); - m_bNewDLCAvailable=false; m_bSeenNewDLCTip=false; @@ -182,15 +188,8 @@ CMinecraftApp::CMinecraftApp() m_iDLCOfferC=0; m_bAllDLCContentRetrieved=true; - InitializeCriticalSection(&csDLCDownloadQueue); m_bAllTMSContentRetrieved=true; m_bTickTMSDLCFiles=true; - InitializeCriticalSection(&csTMSPPDownloadQueue); - InitializeCriticalSection(&csAdditionalModelParts); - InitializeCriticalSection(&csAdditionalSkinBoxes); - InitializeCriticalSection(&csAnimOverrideBitmask); - InitializeCriticalSection(&csMemFilesLock); - InitializeCriticalSection(&csMemTPDLock); InitializeCriticalSection(&m_saveNotificationCriticalSection); m_saveNotificationDepth = 0; @@ -5433,6 +5432,66 @@ void CMinecraftApp::RemoveMemoryTextureFile(const wstring &wName) LeaveCriticalSection(&csMemFilesLock); } +void CMinecraftApp::ScanAndLoadCustomSkins() +{ + DebugPrintf("CustomSkins scanning inited\n"); + + // Try multiple relative paths just in case (game dir or project dir) + const wchar_t* paths[] = { L"CustomSkins\\*.png", L"..\\CustomSkins\\*.png" }; + const wchar_t* folderPaths[] = { L"CustomSkins\\", L"..\\CustomSkins\\" }; + + for (int i = 0; i < 2; ++i) + { + WIN32_FIND_DATAW fd; + HANDLE hFind = FindFirstFileW(paths[i], &fd); + if (hFind != INVALID_HANDLE_VALUE) + { + DebugPrintf("Found CustomSkins folder at: %ls\n", folderPaths[i]); + do { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + wstring filename = fd.cFileName; + wstring fullPath = wstring(folderPaths[i]) + filename; + FILE* f = _wfopen(fullPath.c_str(), L"rb"); + if (f) + { + DebugPrintf("Loading skin file: %ls\n", fullPath.c_str()); + fseek(f, 0, SEEK_END); + DWORD dwSize = (DWORD)ftell(f); + rewind(f); + if (dwSize > 0 && dwSize < 2 * 1024 * 1024) // 2MB limit + { + BYTE* pData = new BYTE[dwSize]; + if (fread(pData, 1, dwSize, f) == dwSize) + { + AddMemoryTextureFile(filename, pData, dwSize); + app.m_customSkinNames.push_back(filename); + DebugPrintf("Successfully loaded custom skin: %ls\n", filename.c_str()); + } + else + { + delete[] pData; + } + } + else + { + DebugPrintf("Skin file too large or empty: %ls (%d bytes)\n", fullPath.c_str(), dwSize); + } + fclose(f); + } + else + { + DebugPrintf("Failed to open file: %ls\n", fullPath.c_str()); + } + } + } while (FindNextFileW(hFind, &fd)); + FindClose(hFind); + return; // Found and scanned + } + } + DebugPrintf("Error: CustomSkins folder not found in common locations.\n"); +} + bool CMinecraftApp::DefaultCapeExists() { wstring wTex=L"Special_Cape.png"; @@ -5468,10 +5527,15 @@ void CMinecraftApp::GetMemFileDetails(const wstring &wName,PBYTE *ppbData,DWORD *ppbData=pData->pbData; *pdwBytes=pData->dwBytes; } + else + { + *ppbData = NULL; + *pdwBytes = 0; + } LeaveCriticalSection(&csMemFilesLock); } -void CMinecraftApp::AddMemoryTPDFile(int iConfig,PBYTE pbData,DWORD dwBytes) +void CMinecraftApp::AddMemoryTPDFile(int iConfig,PBYTE pbData,DWORD dwBytes) { EnterCriticalSection(&csMemTPDLock); // check it's not already in @@ -8772,6 +8836,14 @@ void CMinecraftApp::SetAnimOverrideBitmask(DWORD dwSkinID,unsigned int uiAnimOve DWORD CMinecraftApp::getSkinIdFromPath(const wstring &skin) { + for (size_t i = 0; i < app.m_customSkinNames.size(); ++i) + { + if (app.m_customSkinNames[i] == skin) + { + return 0x20000000 | (DWORD)i; + } + } + bool dlcSkin = false; unsigned int skinId = 0; @@ -8793,6 +8865,7 @@ DWORD CMinecraftApp::getSkinIdFromPath(const wstring &skin) skinId = MAKE_SKIN_BITMASK(dlcSkin, skinId); } + return skinId; } @@ -8805,7 +8878,15 @@ wstring CMinecraftApp::getSkinPathFromId(DWORD skinId) { // 4J Stu - DLC skins are numbered using decimal rather than hex to make it easier to number manually swprintf(chars, 256, L"dlcskin%08d.png", GET_DLC_SKIN_ID_FROM_BITMASK(skinId)); - + } + else if (skinId & 0x20000000) + { + DWORD index = skinId & 0x1FFFFFFF; + if (index < app.m_customSkinNames.size()) + { + return app.m_customSkinNames[index]; + } + swprintf(chars, 256, L"defskin00000000.png"); } else { diff --git a/Minecraft.Client/Common/Consoles_App.h b/Minecraft.Client/Common/Consoles_App.h index 1435601..dca6546 100644 --- a/Minecraft.Client/Common/Consoles_App.h +++ b/Minecraft.Client/Common/Consoles_App.h @@ -71,6 +71,7 @@ public: // storing skin files std::vector vSkinNames; + std::vector m_customSkinNames; DLCManager m_dlcManager; // storing credits text from the DLC @@ -341,6 +342,8 @@ public: void GetMemFileDetails(const wstring &wName,PBYTE *ppbData,DWORD *pdwBytes); bool IsFileInMemoryTextures(const wstring &wName); + void ScanAndLoadCustomSkins(); + // Texture Pack Data files (icon, banner, comparison shot & text) void AddMemoryTPDFile(int iConfig,PBYTE pbData,DWORD dwBytes); void RemoveMemoryTPDFile(int iConfig); diff --git a/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.cpp index 6910dd6..2a25a6e 100644 --- a/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.cpp @@ -10,8 +10,8 @@ #define SKIN_SELECT_PACK_DEFAULT 0 #define SKIN_SELECT_PACK_FAVORITES 1 -//#define SKIN_SELECT_PACK_PLAYER_CUSTOM 1 -#define SKIN_SELECT_MAX_DEFAULTS 2 +#define SKIN_SELECT_PACK_PLAYER_CUSTOM 2 +#define SKIN_SELECT_MAX_DEFAULTS 3 WCHAR *UIScene_SkinSelectMenu::wchDefaultNamesA[]= { @@ -598,6 +598,17 @@ void UIScene_SkinSelectMenu::InputActionOK(unsigned int iPad) } } break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + if (app.m_customSkinNames.size() > 0) + { + wstring selectedSkin = app.m_customSkinNames[m_skinIndex]; + app.SetPlayerSkin(iPad, selectedSkin); + app.SetPlayerCape(iPad, L""); + setCharacterSelected(true); + m_currentSkinPath = selectedSkin; + ui.PlayUISFX(eSFX_Press); + } + break; default: if( m_currentPack != NULL ) { @@ -861,6 +872,27 @@ void UIScene_SkinSelectMenu::handleSkinIndexChanged() m_bNoSkinsToShow=true; } break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + if (app.m_customSkinNames.size() > 0) + { + m_selectedSkinPath = app.m_customSkinNames[m_skinIndex]; + skinName = m_selectedSkinPath; + skinOrigin = L"Custom Skins"; + + if (m_selectedSkinPath.compare(m_currentSkinPath) == 0) + { + setCharacterSelected(true); + } + setCharacterLocked(false); + m_characters[eCharacter_Current].setVisible(true); + m_controlSkinNamePlate.setVisible(true); + } + else + { + m_characters[eCharacter_Current].setVisible(false); + m_bNoSkinsToShow = true; + } + break; } } @@ -1128,6 +1160,13 @@ int UIScene_SkinSelectMenu::getNextSkinIndex(DWORD sourceIndex) nextSkin=0; } + break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + ++nextSkin; + if (nextSkin >= (int)app.m_customSkinNames.size()) + { + nextSkin = 0; + } break; default: ++nextSkin; @@ -1163,6 +1202,16 @@ int UIScene_SkinSelectMenu::getPreviousSkinIndex(DWORD sourceIndex) --previousSkin; } break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + if (previousSkin == 0) + { + previousSkin = (int)app.m_customSkinNames.size() - 1; + } + else + { + --previousSkin; + } + break; default: if(previousSkin==0) { @@ -1261,6 +1310,9 @@ void UIScene_SkinSelectMenu::updatePackDisplay() case SKIN_SELECT_PACK_FAVORITES: setCentreLabel(app.GetString(IDS_FAVORITES_SKIN_PACK)); break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + setCentreLabel(L"Custom Skins"); + break; } } @@ -1280,6 +1332,9 @@ void UIScene_SkinSelectMenu::updatePackDisplay() case SKIN_SELECT_PACK_FAVORITES: setRightLabel(app.GetString(IDS_FAVORITES_SKIN_PACK)); break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + setRightLabel(L"Custom Skins"); + break; } } @@ -1299,6 +1354,9 @@ void UIScene_SkinSelectMenu::updatePackDisplay() case SKIN_SELECT_PACK_FAVORITES: setLeftLabel(app.GetString(IDS_FAVORITES_SKIN_PACK)); break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + setLeftLabel(L"Custom Skins"); + break; } } diff --git a/Minecraft.Client/Common/XUI/XUI_SkinSelect.cpp b/Minecraft.Client/Common/XUI/XUI_SkinSelect.cpp index ee70661..2c1c7e1 100644 --- a/Minecraft.Client/Common/XUI/XUI_SkinSelect.cpp +++ b/Minecraft.Client/Common/XUI/XUI_SkinSelect.cpp @@ -4,8 +4,8 @@ #define SKIN_SELECT_PACK_DEFAULT 0 #define SKIN_SELECT_PACK_FAVORITES 1 -//#define SKIN_SELECT_PACK_PLAYER_CUSTOM 1 -#define SKIN_SELECT_MAX_DEFAULTS 2 +#define SKIN_SELECT_PACK_PLAYER_CUSTOM 2 +#define SKIN_SELECT_MAX_DEFAULTS 3 WCHAR *CScene_SkinSelect::wchDefaultNamesA[]= { @@ -287,14 +287,29 @@ HRESULT CScene_SkinSelect::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandle } else { - app.SetPlayerSkin(pInputData->UserIndex, skinFile->getPath()); - app.SetPlayerCape(pInputData->UserIndex, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); - m_selectedGroup.SetShow( TRUE ); - m_currentSkinPath = app.GetPlayerSkinName(m_iPad); - m_originalSkinId = app.GetPlayerSkinId(m_iPad); + if (m_packIndex == SKIN_SELECT_PACK_PLAYER_CUSTOM) + { + if (app.m_customSkinNames.size() > 0) + { + wstring selectedSkin = app.m_customSkinNames[m_skinIndex]; + app.SetPlayerSkin(pInputData->UserIndex, selectedSkin); + app.SetPlayerCape(pInputData->UserIndex, L""); + m_selectedGroup.SetShow(TRUE); + m_currentSkinPath = selectedSkin; + m_originalSkinId = 0; + } + } + else + { + app.SetPlayerSkin(pInputData->UserIndex, skinFile->getPath()); + app.SetPlayerCape(pInputData->UserIndex, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + m_selectedGroup.SetShow( TRUE ); + m_currentSkinPath = app.GetPlayerSkinName(m_iPad); + m_originalSkinId = app.GetPlayerSkinId(m_iPad); - // push this onto the favorite list - AddFavoriteSkin(m_iPad,GET_DLC_SKIN_ID_FROM_BITMASK(m_originalSkinId)); + // push this onto the favorite list + AddFavoriteSkin(m_iPad,GET_DLC_SKIN_ID_FROM_BITMASK(m_originalSkinId)); + } } } else @@ -743,7 +758,31 @@ void CScene_SkinSelect::handleSkinIndexChanged() bNoSkinsToShow=true; } break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + if (app.m_customSkinNames.size() > 0) + { + m_selectedSkinPath = app.m_customSkinNames[m_skinIndex]; + skinName = m_selectedSkinPath; + skinOrigin = L"Custom Skins"; + if (m_selectedSkinPath.compare(m_currentSkinPath) == 0) + { + m_selectedGroup.SetShow(TRUE); + } + else + { + m_selectedGroup.SetShow(FALSE); + } + m_imagePadlock.SetShow(FALSE); + m_previewControl->SetShow(TRUE); + m_skinDetails.SetShow(TRUE); + } + else + { + m_previewControl->SetShow(FALSE); + bNoSkinsToShow = true; + } + break; } } m_text.SetText(skinName.c_str()); @@ -1041,6 +1080,9 @@ void CScene_SkinSelect::handlePackIndexChanged() } } break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + // No extra logic needed for custom pack index change for now + break; default: break; } @@ -1071,6 +1113,9 @@ void CScene_SkinSelect::updatePackDisplay() case SKIN_SELECT_PACK_FAVORITES: m_packCenter.SetText(app.GetString(IDS_FAVORITES_SKIN_PACK)); break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + m_packCenter.SetText(L"Custom Skins"); + break; } } @@ -1090,6 +1135,9 @@ void CScene_SkinSelect::updatePackDisplay() case SKIN_SELECT_PACK_FAVORITES: m_packRight.SetText(app.GetString(IDS_FAVORITES_SKIN_PACK)); break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + m_packRight.SetText(L"Custom Skins"); + break; } } @@ -1109,6 +1157,9 @@ void CScene_SkinSelect::updatePackDisplay() case SKIN_SELECT_PACK_FAVORITES: m_packLeft.SetText(app.GetString(IDS_FAVORITES_SKIN_PACK)); break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + m_packLeft.SetText(L"Custom Skins"); + break; } } @@ -1191,6 +1242,13 @@ int CScene_SkinSelect::getNextSkinIndex(DWORD sourceIndex) nextSkin=0; } + break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + ++nextSkin; + if (nextSkin >= (int)app.m_customSkinNames.size()) + { + nextSkin = 0; + } break; default: ++nextSkin; @@ -1226,6 +1284,16 @@ int CScene_SkinSelect::getPreviousSkinIndex(DWORD sourceIndex) --previousSkin; } break; + case SKIN_SELECT_PACK_PLAYER_CUSTOM: + if (previousSkin == 0) + { + previousSkin = (int)app.m_customSkinNames.size() - 1; + } + else + { + --previousSkin; + } + break; default: if(previousSkin==0) {