You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
516 lines
9.9 KiB
516 lines
9.9 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
// |
|
// Half-Life Model Viewer (c) 1999 by Mete Ciragan |
|
// |
|
// file: pakviewer.cpp |
|
// last modified: May 04 1999, Mete Ciragan |
|
// copyright: The programs and associated files contained in this |
|
// distribution were developed by Mete Ciragan. The programs |
|
// are not in the public domain, but they are freely |
|
// distributable without licensing fees. These programs are |
|
// provided without guarantee or warrantee expressed or |
|
// implied. |
|
// |
|
// version: 1.2 |
|
// |
|
// email: mete@swissquake.ch |
|
// web: http://www.swissquake.ch/chumbalum-soft/ |
|
// |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <mxtk/mx.h> |
|
#include "pakviewer.h" |
|
#include "mdlviewer.h" |
|
// #include "GlWindow.h" |
|
#include "StudioModel.h" |
|
#include "ControlPanel.h" |
|
#include "FileAssociation.h" |
|
|
|
|
|
|
|
int |
|
pak_ExtractFile (const char *pakFile, const char *lumpName, char *outFile) |
|
{ |
|
FILE *file = fopen (pakFile, "rb"); |
|
if (!file) |
|
return 0; |
|
|
|
int ident, dirofs, dirlen; |
|
|
|
fread (&ident, sizeof (int), 1, file); |
|
if (ident != (int) (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P')) |
|
{ |
|
fclose (file); |
|
return 0; |
|
} |
|
|
|
fread (&dirofs, sizeof (int), 1, file); |
|
fread (&dirlen, sizeof (int), 1, file); |
|
|
|
fseek (file, dirofs, SEEK_SET); |
|
int numLumps = dirlen / 64; |
|
|
|
for (int i = 0; i < numLumps; i++) |
|
{ |
|
char name[56]; |
|
int filepos, filelen; |
|
|
|
fread (name, 56, 1, file); |
|
fread (&filepos, sizeof (int), 1, file); |
|
fread (&filelen, sizeof (int), 1, file); |
|
|
|
if (!mx_strcasecmp (name, lumpName)) |
|
{ |
|
FILE *out = fopen (outFile, "wb"); |
|
if (!out) |
|
{ |
|
fclose (file); |
|
return 0; |
|
} |
|
|
|
fseek (file, filepos, SEEK_SET); |
|
|
|
while (filelen--) |
|
fputc (fgetc (file), out); |
|
|
|
fclose (out); |
|
fclose (file); |
|
|
|
return 1; |
|
} |
|
} |
|
|
|
fclose (file); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
|
|
PAKViewer::PAKViewer (mxWindow *window) |
|
: mxWindow (window, 0, 0, 0, 0, "", mxWindow::Normal) |
|
{ |
|
strcpy (d_pakFile, ""); |
|
strcpy (d_currLumpName, ""); |
|
|
|
tvPAK = new mxTreeView (this, 0, 0, 0, 0, IDC_PAKVIEWER); |
|
pmMenu = new mxPopupMenu (); |
|
pmMenu->add ("Load Model", 1); |
|
pmMenu->addSeparator (); |
|
pmMenu->add ("Load Background", 2); |
|
pmMenu->add ("Load Ground", 3); |
|
pmMenu->addSeparator (); |
|
pmMenu->add ("Play Sound", 4); |
|
pmMenu->addSeparator (); |
|
pmMenu->add ("Extract File...", 5); |
|
setLoadEntirePAK (true); |
|
|
|
setVisible (false); |
|
} |
|
|
|
|
|
|
|
PAKViewer::~PAKViewer () |
|
{ |
|
//tvPAK->remove (0); |
|
//tvPAK->remove(); |
|
closePAKFile (); |
|
} |
|
|
|
|
|
|
|
void |
|
_makeTempFileName (char *str, const char *suffix) |
|
{ |
|
strcpy (str, mx_gettemppath ()); |
|
|
|
strcat (str, "/hltempmodel"); |
|
strcat (str, suffix); |
|
} |
|
|
|
|
|
|
|
int |
|
PAKViewer::handleEvent (mxEvent *event) |
|
{ |
|
switch (event->event) |
|
{ |
|
case mxEvent::Action: |
|
{ |
|
switch (event->action) |
|
{ |
|
case IDC_PAKVIEWER: // tvPAK |
|
if (event->flags & mxEvent::RightClicked) |
|
{ |
|
pmMenu->setEnabled (1, strstr (d_currLumpName, ".mdl") != 0); |
|
pmMenu->setEnabled (2, strstr (d_currLumpName, ".tga") != 0); |
|
pmMenu->setEnabled (3, strstr (d_currLumpName, ".tga") != 0); |
|
pmMenu->setEnabled (4, strstr (d_currLumpName, ".wav") != 0); |
|
int ret = pmMenu->popup (tvPAK, event->x, event->y); |
|
switch (ret) |
|
{ |
|
case 1: |
|
OnLoadModel (); |
|
break; |
|
|
|
case 2: |
|
OnLoadTexture (0); |
|
break; |
|
|
|
case 3: |
|
OnLoadTexture (1); |
|
break; |
|
|
|
case 4: |
|
OnPlaySound (); |
|
break; |
|
|
|
case 5: |
|
OnExtract (); |
|
break; |
|
} |
|
} |
|
else if (event->flags & mxEvent::DoubleClicked) |
|
{ |
|
OnPAKViewer (); |
|
char e[16]; |
|
|
|
strncpy (e, mx_getextension (d_currLumpName), 16); |
|
int mode = g_FileAssociation->getMode (&e[1]); |
|
if (mode == -1) |
|
return 1; |
|
|
|
char *program = g_FileAssociation->getProgram (&e[1]); |
|
|
|
#ifdef WIN32 |
|
if (mode == 0) |
|
{ |
|
char str[256]; |
|
_makeTempFileName (str, e); |
|
if (!pak_ExtractFile (d_pakFile, d_currLumpName, str)) |
|
mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
else |
|
{ |
|
if (program) |
|
{ |
|
char path[256]; |
|
strcpy (path, program); |
|
strcat (path, " "); |
|
strcat (path, str); |
|
if ((int) WinExec (path, SW_SHOW) <= 32) |
|
mxMessageBox (this, "Error executing specified program.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
} |
|
} |
|
} |
|
|
|
// associated program |
|
else if (mode == 1) |
|
{ |
|
char str[256]; |
|
_makeTempFileName (str, e); |
|
if (!pak_ExtractFile (d_pakFile, d_currLumpName, str)) |
|
mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
else |
|
if ((int) ShellExecute ((HWND) getHandle (), "open", str, 0, 0, SW_SHOW) <= 32) |
|
mxMessageBox (this, "Error executing document with associated program.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
} |
|
|
|
// HLMV default |
|
else |
|
#endif |
|
if (mode == 2) |
|
{ |
|
if (!strcmp (e, ".mdl")) |
|
OnLoadModel (); |
|
|
|
else if (!strcmp (e, ".tga")) |
|
OnLoadTexture (0); |
|
|
|
else if (!strcmp (e, ".wav")) |
|
OnPlaySound (); |
|
|
|
return 1; |
|
} |
|
} |
|
|
|
return OnPAKViewer (); |
|
} // event->action |
|
} // mxEvent::Action |
|
break; |
|
|
|
case mxEvent::Size: |
|
{ |
|
tvPAK->setBounds (0, 0, event->width, event->height); |
|
} // mxEvent::Size |
|
break; |
|
|
|
} // event->event |
|
|
|
return 1; |
|
} |
|
|
|
|
|
|
|
int |
|
PAKViewer::OnPAKViewer () |
|
{ |
|
mxTreeViewItem *tvi = tvPAK->getSelectedItem (); |
|
if (tvi) |
|
{ |
|
strcpy (d_currLumpName, tvPAK->getLabel (tvi)); |
|
|
|
// find the full lump name |
|
mxTreeViewItem *tviParent = tvPAK->getParent (tvi); |
|
char tmp[128]; |
|
while (tviParent) |
|
{ |
|
strcpy (tmp, d_currLumpName); |
|
strcpy (d_currLumpName, tvPAK->getLabel (tviParent)); |
|
strcat (d_currLumpName, "/"); |
|
strcat (d_currLumpName, tmp); |
|
tviParent = tvPAK->getParent (tviParent); |
|
} |
|
|
|
if (!d_loadEntirePAK) |
|
{ |
|
// finally insert "models/" |
|
strcpy (tmp, d_currLumpName); |
|
strcpy (d_currLumpName, "models/"); |
|
strcat (d_currLumpName, tmp); |
|
} |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
|
|
|
|
int PAKViewer::OnLoadModel () |
|
{ |
|
static char str2[256]; |
|
char suffix[16]; |
|
|
|
strcpy (suffix, ".mdl"); |
|
_makeTempFileName (str2, suffix); |
|
|
|
if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2)) |
|
{ |
|
mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
return 1; |
|
} |
|
|
|
g_pStudioModel->FreeModel ( false ); |
|
if( !g_pStudioModel->LoadModel (str2) ) |
|
{ |
|
mxMessageBox (this, "Error reading model header.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
return 1; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
|
|
|
|
int PAKViewer::OnLoadTexture (int pos) |
|
{ |
|
static char str2[256]; |
|
char suffix[16] = ""; |
|
|
|
if (strstr (d_currLumpName, ".tga")) |
|
sprintf (suffix, "%d%s", pos, ".tga"); |
|
|
|
_makeTempFileName (str2, suffix); |
|
|
|
if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2)) |
|
{ |
|
mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
return 1; |
|
} |
|
|
|
if (0 /* g_MDLViewer->getGlWindow ()->loadTexture (str2, pos) */) |
|
{ |
|
if (pos == 0) |
|
g_ControlPanel->setShowBackground (true); |
|
else |
|
g_ControlPanel->setShowGround (true); |
|
} |
|
else |
|
mxMessageBox (this, "Error loading texture.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
|
|
return 1; |
|
} |
|
|
|
|
|
|
|
int |
|
PAKViewer::OnPlaySound () |
|
{ |
|
#ifdef WIN32 |
|
static char str2[256]; |
|
char suffix[16] = ""; |
|
|
|
// stop any playing sound |
|
PlaySound (0, 0, SND_FILENAME | SND_ASYNC); |
|
|
|
if (strstr (d_currLumpName, ".wav")) |
|
sprintf (suffix, "%d%s", 44, ".wav"); |
|
|
|
_makeTempFileName (str2, suffix); |
|
|
|
if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2)) |
|
{ |
|
mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
return 1; |
|
} |
|
|
|
PlaySound (str2, 0, SND_FILENAME | SND_ASYNC); |
|
|
|
#endif |
|
return 1; |
|
} |
|
|
|
|
|
|
|
int |
|
PAKViewer::OnExtract () |
|
{ |
|
char *ptr = (char *) mxGetSaveFileName (this, "", "*.*"); |
|
if (ptr) |
|
{ |
|
if (!pak_ExtractFile (d_pakFile, d_currLumpName, ptr)) |
|
mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR); |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
|
|
|
|
int |
|
_compare(const void *arg1, const void *arg2) |
|
{ |
|
if (strchr ((char *) arg1, '/') && !strchr ((char *) arg2, '/')) |
|
return -1; |
|
|
|
else if (!strchr ((char *) arg1, '/') && strchr ((char *) arg2, '/')) |
|
return 1; |
|
|
|
else |
|
return strcmp ((char *) arg1, (char *) arg2); |
|
} |
|
|
|
|
|
|
|
bool |
|
PAKViewer::openPAKFile (const char *pakFile) |
|
{ |
|
FILE *file = fopen (pakFile, "rb"); |
|
if (!file) |
|
return false; |
|
|
|
int ident, dirofs, dirlen; |
|
|
|
// check for id |
|
fread (&ident, sizeof (int), 1, file); |
|
if (ident != (int) (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P')) |
|
{ |
|
fclose (file); |
|
return false; |
|
} |
|
|
|
// load lumps |
|
fread (&dirofs, sizeof (int), 1, file); |
|
fread (&dirlen, sizeof (int), 1, file); |
|
int numLumps = dirlen / 64; |
|
|
|
fseek (file, dirofs, SEEK_SET); |
|
lump_t *lumps = new lump_t[numLumps]; |
|
if (!lumps) |
|
{ |
|
fclose (file); |
|
return false; |
|
} |
|
|
|
fread (lumps, sizeof (lump_t), numLumps, file); |
|
fclose (file); |
|
|
|
qsort (lumps, numLumps, sizeof (lump_t), _compare); |
|
|
|
// save pakFile for later |
|
strcpy (d_pakFile, pakFile); |
|
|
|
tvPAK->remove (0); |
|
|
|
char namestack[32][32]; |
|
mxTreeViewItem *tvistack[32]; |
|
for (int k = 0; k < 32; k++) |
|
{ |
|
strcpy (namestack[k], ""); |
|
tvistack[k] = 0; |
|
} |
|
|
|
for (int i = 0; i < numLumps; i++) |
|
{ |
|
if (d_loadEntirePAK || !strncmp (lumps[i].name, "models", 6)) |
|
{ |
|
char *tok; |
|
if (d_loadEntirePAK) |
|
tok = &lumps[i].name[0]; |
|
else |
|
tok = &lumps[i].name[7]; |
|
|
|
int i = 1; |
|
while (tok) |
|
{ |
|
char *end = strchr (tok, '/'); |
|
if (end) |
|
*end = '\0'; |
|
|
|
if (strcmp (namestack[i], tok)) |
|
{ |
|
strcpy (namestack[i], tok); |
|
/* |
|
if (i == 0) |
|
tvistack[i] = tvPAK->add (0, tok); |
|
else*/ |
|
tvistack[i] = tvPAK->add (tvistack[i - 1], tok); |
|
|
|
for (int j = i + 1; j < 32; j++) |
|
{ |
|
strcpy (namestack[j], ""); |
|
tvistack[j] = 0; |
|
} |
|
} |
|
|
|
++i; |
|
|
|
if (end) |
|
tok = end + 1; |
|
else |
|
tok = 0; |
|
} |
|
} |
|
} |
|
|
|
delete[] lumps; |
|
|
|
setVisible (true); |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
void |
|
PAKViewer::closePAKFile () |
|
{ |
|
strcpy (d_pakFile, ""); |
|
setVisible (false); |
|
}
|
|
|