431 lines
15 KiB
C++
431 lines
15 KiB
C++
|
/*
|
||
|
** 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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "StdAfx.h"
|
||
|
#include "priv.h"
|
||
|
#include "shellext.h"
|
||
|
#include "resource.h"
|
||
|
#include <stdio.h>
|
||
|
#include "wdump.h"
|
||
|
#include "WdumpDoc.h"
|
||
|
#include "W3D_File.h"
|
||
|
#include <commctrl.h>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//===============================================================
|
||
|
// *********** 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
|
||
|
}
|