/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ #include "StdAfx.h" #include "priv.h" #include "shellext.h" #include "resource.h" #include #include "wdump.h" #include "WdumpDoc.h" #include "W3D_File.h" #include //=============================================================== // *********** G L O B A L S ************************ extern UINT g_DllRefCount; // Reference count of this DLL. extern HINSTANCE g_DllInstance; // Handle to this DLL itself. //=============================================================== // *********** F U N C T I O N S ********************* /////////////////////////////////////// UINT CALLBACK W3DPageCallback(HWND hWnd, UINT uMessage, LPPROPSHEETPAGE ppsp){ switch(uMessage){ case PSPCB_CREATE: return TRUE; case PSPCB_RELEASE:{ if (ppsp->lParam){ ((LPCSHELLEXT)(ppsp->lParam))->Release(); } return TRUE; } } return TRUE; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ShowHideControls(HWND hDlg, bool show){ HWND hControl = GetWindow(hDlg, GW_CHILD); while(hControl != NULL){ ShowWindow(hControl,show?SW_SHOW:SW_HIDE); hControl = GetWindow(hControl, GW_HWNDNEXT); } ShowWindow(GetDlgItem(hDlg, IDC_NODATA),show?SW_HIDE:SW_SHOW ); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void GetItemName(ChunkItem *pItem, int id_of_interest, void* pInfo, int sizeof_struct, int& found ){ if(pItem->ID == id_of_interest){ BYTE *byte_ptr = (BYTE*) pInfo; byte_ptr += sizeof_struct* found; memcpy(byte_ptr,pItem->Data,pItem->Length); found ++; } //Get all Sibitems for this item POSITION p = pItem->Chunks.GetHeadPosition(); while(p != 0) { ChunkItem *subitem = pItem->Chunks.GetNext(p); GetItemName(subitem, id_of_interest, pInfo, sizeof_struct,found); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// BOOL CALLBACK AnimPageDlgProc(HWND hDlg,UINT uMessage, WPARAM wParam, LPARAM lParam){ LPPROPSHEETPAGE psp=(LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER); UINT iIndex=0; LPCSHELLEXT lpcs; char buf[MAX_PATH]; switch (uMessage){ case WM_INITDIALOG:{ SetWindowLong(hDlg, DWL_USER, lParam); psp = (LPPROPSHEETPAGE)lParam; lpcs = (LPCSHELLEXT)psp->lParam; if(!lpcs->m_FileInMemory){ lpcs->Read_SelectedFile(); } ChunkData* pData = &(lpcs->m_WdumpDocument.m_ChunkData); assert(pData); POSITION ptr = pData->Chunks.GetHeadPosition();; ASSERT(ptr); W3dAnimHeaderStruct* pAnim = lpcs->m_AnimInfos; int id_of_interest = W3D_CHUNK_ANIMATION_HEADER; int sizeof_struct = sizeof W3dAnimHeaderStruct; int found_animations(0); while(ptr){ ChunkItem *pItem = pData->Chunks.GetNext(ptr); GetItemName(pItem, id_of_interest, (void*)lpcs->m_AnimInfos, sizeof_struct, found_animations ); } if(found_animations){ //So far assuming only one animation per file SetDlgItemText(hDlg, IDC_ANIMNAME, pAnim->Name); SetDlgItemText(hDlg, IDC_HNAME, pAnim->HierarchyName); SetDlgItemInt(hDlg, IDC_NUMFRAMES, pAnim->NumFrames,FALSE); //FrameRate sprintf(buf,"%i fps",pAnim->FrameRate,FALSE); SetDlgItemText(hDlg, IDC_FRAMERATE, buf); //SetDlgItemInt(hDlg, IDC_FRAMERATE, pAnim->FrameRate,FALSE); //version sprintf(buf,"%d.%d",W3D_GET_MAJOR_VERSION(pAnim->Version),W3D_GET_MINOR_VERSION(pAnim->Version)); SetDlgItemText(hDlg, IDC_ANIMVERSION, buf); } //Hierarchies ptr = pData->Chunks.GetHeadPosition();; ASSERT(ptr); id_of_interest = W3D_CHUNK_HIERARCHY_HEADER; //So far assuming only one hierarchy per file W3dHierarchyStruct* pHInfo = lpcs->m_Hierarchies; ASSERT(pHInfo); sizeof_struct = sizeof W3dHierarchyStruct; int found_hierarchies(0); while(ptr){ ChunkItem *pItem = pData->Chunks.GetNext(ptr); GetItemName(pItem, id_of_interest, (void*)pHInfo, sizeof_struct, found_hierarchies ); } if(found_hierarchies){ SetDlgItemText(hDlg, IDC_HIERARCHYNAME, pHInfo->Name); SetDlgItemInt(hDlg, IDC_NUMPIVOTS, pHInfo->NumPivots,FALSE); sprintf(buf,"%d.%d",W3D_GET_MAJOR_VERSION(pHInfo->Version),W3D_GET_MINOR_VERSION(pHInfo->Version)); SetDlgItemText(hDlg, IDC_HIERARCHYVERSION, buf); sprintf(buf, "(%.3f, %.3f, %.3f)", pHInfo->Center.X, pHInfo->Center.Y, pHInfo->Center.Z); SetDlgItemText(hDlg, IDC_HCENTER, buf); } ShowHideControls(hDlg, (found_animations + found_hierarchies)!= 0); break; } case WM_DESTROY:{ RemoveProp(hDlg, "ID"); break; } case WM_COMMAND: switch (LOWORD(wParam)){ case IDC_TOP3D: //SetProp(hDlg, "ID", (HANDLE)lParam); break; default: break; } break; case WM_NOTIFY: switch (((NMHDR FAR *)lParam)->code){ case PSN_SETACTIVE: break; case PSN_APPLY: //User has clicked the OK or Apply button so we'll //update the icon information in the .W3D file lpcs = (LPCSHELLEXT)psp->lParam; //Ask the shell to refresh the icon list... //SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, 0, 0); break; default: break; } break; default: return FALSE; } return TRUE; } //===================================================================================================== void SetDlgMeshParams(HWND hDlg, W3dMeshHeader3Struct*pInfo){ SetDlgItemText(hDlg,IDC_MESHNAME,pInfo->MeshName); SetDlgItemText(hDlg,IDC_CONTAINER,pInfo->ContainerName); SetDlgItemInt(hDlg,IDC_NUM_MATERIALS, pInfo->NumMaterials,FALSE); SetDlgItemInt(hDlg,IDC_NUM_POLYS, pInfo->NumTris,FALSE); SetDlgItemInt(hDlg,IDC_NUM_VERTICES, pInfo->NumVertices, FALSE); SetDlgItemInt(hDlg,IDC_NUM_MATERIALS, pInfo->NumMaterials,FALSE); char msg[MAX_PATH]; sprintf(msg, "%.3f", pInfo->SphRadius); SetDlgItemText(hDlg,IDC_SPHERERADIUS,msg); //3D Coords //Center sprintf(msg, "(%.3f, %.3f, %.3f)", pInfo->SphCenter.X, pInfo->SphCenter.Y, pInfo->SphCenter.Z); SetDlgItemText(hDlg, IDC_SPHERECENTER, msg); //Min sprintf(msg, "(%.3f, %.3f, %.3f)", pInfo->Min.X, pInfo->Min.Y, pInfo->Min.Z); SetDlgItemText(hDlg, IDC_MINDIM, msg); //Max sprintf(msg, "(%.3f, %.3f, %.3f)", pInfo->Max.X, pInfo->Max.Y, pInfo->Max.Z); SetDlgItemText(hDlg, IDC_MAXDIM, msg); sprintf(msg,"%d.%d",W3D_GET_MAJOR_VERSION(pInfo->Version),W3D_GET_MINOR_VERSION(pInfo->Version)); SetDlgItemText(hDlg, IDC_VERSION, msg); } //========================================================================================================== BOOL CALLBACK MeshPageDlgProc(HWND hDlg,UINT uMessage, WPARAM wParam, LPARAM lParam){ LPPROPSHEETPAGE psp=(LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER); UINT iIndex=0; LPCSHELLEXT lpcs; switch (uMessage){ case WM_INITDIALOG:{ SetWindowLong(hDlg, DWL_USER, lParam); psp = (LPPROPSHEETPAGE)lParam; lpcs = (LPCSHELLEXT)psp->lParam; if(!lpcs->m_FileInMemory){ lpcs->Read_SelectedFile(); } ChunkData* pData = &(lpcs->m_WdumpDocument.m_ChunkData); assert(pData); POSITION ptr = pData->Chunks.GetHeadPosition(); ASSERT(ptr); //Look for mesh headers W3dMeshHeader3Struct* pInfo = lpcs->m_Meshes; int id_of_interest = W3D_CHUNK_MESH_HEADER3; int sizeof_struct = sizeof W3dMeshHeader3Struct; while(ptr){ ChunkItem *pItem = pData->Chunks.GetNext(ptr); ASSERT(lpcs->m_FoundMeshes < MAX_MESH); GetItemName(pItem, id_of_interest, (void*)(pInfo),sizeof_struct, lpcs->m_FoundMeshes ); } if(lpcs->m_FoundMeshes){ SetDlgItemInt(hDlg,IDC_MESHNUMBER, 0,FALSE); //Look for Textures char pTextureInfo[MAX_TEXUTRE_NAME_LEN * MAX_MESH]; id_of_interest = W3D_CHUNK_TEXTURE_NAME; int found_textures(0); int sizeof_struct = MAX_TEXUTRE_NAME_LEN; ptr = pData->Chunks.GetHeadPosition(); while(ptr){ ChunkItem *pItem = pData->Chunks.GetNext(ptr); GetItemName(pItem, id_of_interest, (void*)pTextureInfo, sizeof_struct, found_textures ); } for(int t_count(0); t_count < found_textures; t_count ++){ if(lpcs->NotAdded(&(pTextureInfo[t_count*MAX_TEXUTRE_NAME_LEN]))){ char* text = lpcs->m_Textures[lpcs->m_NumAdded-1].LockBuffer(); SendDlgItemMessage(hDlg, IDC_TEXTURELIST, LB_ADDSTRING,(WPARAM) 0L, (LPARAM)text);//(pTextureInfo[t_count].name)) ; lpcs->m_Textures[lpcs->m_NumAdded-1].ReleaseBuffer(); } } SetDlgItemInt(hDlg,IDC_NUM_MESHES, lpcs->m_FoundMeshes,FALSE); SetDlgMeshParams(hDlg, pInfo); //Set Spin range SendDlgItemMessage(hDlg, IDC_MESHSPIN, UDM_SETRANGE, (WPARAM)0L, (LPARAM)MAKELONG(lpcs->m_FoundMeshes-1,0)); } ShowHideControls(hDlg, lpcs->m_FoundMeshes != 0); break; } case WM_DESTROY: RemoveProp(hDlg, "ID"); break; case WM_COMMAND: switch (LOWORD(wParam)){ case IDC_TOP3D: //SetProp(hDlg, "ID", (HANDLE)lParam); break; default: break; } break; case WM_NOTIFY: lpcs = (LPCSHELLEXT)psp->lParam; switch (((NMHDR FAR *)lParam)->code){ case PSN_SETACTIVE: break; case PSN_APPLY: //Ask the shell to refresh the icon list... SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, 0, 0); break; case UDN_DELTAPOS:{ lpcs = (LPCSHELLEXT)psp->lParam; W3dMeshHeader3Struct* pInfo = lpcs->m_Meshes; LPNMUPDOWN lpUD = (LPNMUPDOWN) lParam; int mesh_num = lpUD->iDelta + lpUD->iPos; if(mesh_num >= 0 && mesh_num < lpcs->m_FoundMeshes){ SetDlgMeshParams(hDlg, pInfo+mesh_num); } break; } default: break; } break; default: return FALSE; } return TRUE; } ///////////////////////////////////////////////////////////// BOOL CALLBACK PreviewPageDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam){ LPPROPSHEETPAGE psp=(LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER); UINT iIndex(0); LPCSHELLEXT lpcs; switch (uMessage){ case WM_INITDIALOG:{ SetWindowLong(hDlg, DWL_USER, lParam); psp = (LPPROPSHEETPAGE)lParam; lpcs = (LPCSHELLEXT)psp->lParam; if(!lpcs->m_FileInMemory){ lpcs->Read_SelectedFile(); } break; } case WM_DESTROY:{ RemoveProp(hDlg, "ID"); break; } case WM_COMMAND: switch (LOWORD(wParam)){ case IDC_TOP3D: //SetProp(hDlg, "ID", (HANDLE)lParam); break; default: break; } break; case WM_NOTIFY: switch (((NMHDR FAR *)lParam)->code){ case PSN_SETACTIVE: break; case PSN_APPLY: //User has clicked the OK or Apply button so we'll //update the icon information in the .W3D file lpcs = (LPCSHELLEXT)psp->lParam; //Ask the shell to refresh the icon list... SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, 0, 0); break; default: break; } break; default: return FALSE; } return TRUE; } // PURPOSE: Called by the shell just before the property sheet is displayed. STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, //Pointer to the Shell's AddPage function LPARAM lParam){ //Passed as second parameter to lpfnAddPage m_FileInMemory = false; PROPSHEETPAGE psp; HPROPSHEETPAGE hpage; FORMATETC fmte = {CF_HDROP,(DVTARGETDEVICE FAR *)NULL,DVASPECT_CONTENT,-1, TYMED_HGLOBAL }; STGMEDIUM medium; HRESULT hres = 0; // char buf[MAX_PATH]; if (m_pDataObj){ //Paranoid check, m_pDataObj should have something by now... hres = m_pDataObj->GetData(&fmte, &medium); } if (SUCCEEDED(hres)){ //Find out how many files the user has selected... UINT cbFiles = 0; LPCSHELLEXT lpcsext = this; if (medium.hGlobal){ cbFiles = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, 0, 0); } if (cbFiles < 2){ if (cbFiles){ DragQueryFile((HDROP)medium.hGlobal,0, m_SelectedFile, sizeof(m_SelectedFile)); } psp.dwSize = sizeof(psp); // no extra data. psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK; psp.hInstance = g_DllInstance; psp.pszTemplate = MAKEINTRESOURCE(IDD_MESH); psp.hIcon = 0; psp.pszTitle = " Meshes "; psp.pfnDlgProc = MeshPageDlgProc; psp.pcRefParent = &g_DllRefCount; psp.pfnCallback = W3DPageCallback; psp.lParam = (LPARAM)lpcsext; AddRef(); hpage = CreatePropertySheetPage(&psp); if(hpage){ if (!lpfnAddPage(hpage, lParam)){ DestroyPropertySheetPage(hpage); Release(); } } //Animation Page psp.pszTemplate = MAKEINTRESOURCE(IDD_ANIMPAGE); psp.pszTitle = "Animations"; psp.pfnDlgProc = AnimPageDlgProc; AddRef(); hpage = CreatePropertySheetPage(&psp); if(hpage){ if (!lpfnAddPage(hpage, lParam)){ DestroyPropertySheetPage(hpage); Release(); } } //Preview Page psp.pszTemplate = MAKEINTRESOURCE(IDD_PREVIEW); psp.pszTitle = " Preview "; psp.pfnDlgProc = PreviewPageDlgProc; AddRef(); hpage = CreatePropertySheetPage(&psp); if(hpage){ if (!lpfnAddPage(hpage, lParam)){ DestroyPropertySheetPage(hpage); Release(); } } } } return NOERROR; } // PURPOSE: Called by the shell only for Control Panel property sheet STDMETHODIMP CShellExt::ReplacePage(UINT uPageID, //ID of page to be replaced LPFNADDPROPSHEETPAGE lpfnReplaceWith, //Pointer to the Shell's Replace function LPARAM lParam){ //Passed as second parameter to lpfnReplaceWith return E_FAIL;//we don't support this function. It should never be }