// // 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 #include #include #include #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); 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 || strstr (d_currLumpName, ".bmp")); pmMenu->setEnabled (3, strstr (d_currLumpName, ".tga") != 0 || strstr (d_currLumpName, ".bmp")); 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 (TEXTURE_BACKGROUND); break; case 3: OnLoadTexture (TEXTURE_GROUND); 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 (!mx_strcasecmp (e, ".mdl")) OnLoadModel (); else if (!mx_strcasecmp (e, ".tga") || !mx_strcasecmp (e, ".bmp")) OnLoadTexture (TEXTURE_BACKGROUND); else if (!mx_strcasecmp (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_studioModel.FreeModel (); studiohdr_t *hdr = g_studioModel.LoadModel (str2); if (!hdr) { // mxMessageBox (this, "Error reading model header.", g_appTitle, MX_MB_OK | MX_MB_ERROR); return 1; } if (hdr->numtextures == 0) { char texturename[256]; strcpy( texturename, d_currLumpName ); strcpy( &texturename[strlen(texturename) - 4], "T.mdl" ); strcpy (suffix, "T.mdl"); _makeTempFileName (str2, suffix); if (!pak_ExtractFile (d_pakFile, texturename, str2)) { g_studioModel.FreeModel (); mxMessageBox (this, "Error extracting from PAK file 1.", g_appTitle, MX_MB_OK | MX_MB_ERROR); return 1; } } if (hdr->numseqgroups > 1) { for (int i = 1; i < hdr->numseqgroups; i++) { char seqgroupname[256]; strcpy( seqgroupname, d_currLumpName ); sprintf( &seqgroupname[strlen(seqgroupname) - 4], "%02d.mdl", i ); sprintf (suffix, "%02d.mdl", i); _makeTempFileName (str2, suffix); if (!pak_ExtractFile (d_pakFile, seqgroupname, str2)) { g_studioModel.FreeModel (); mxMessageBox (this, "Error extracting from PAK file 2.", g_appTitle, MX_MB_OK | MX_MB_ERROR); return 1; } } } g_studioModel.FreeModel (); strcpy (suffix, ".mdl"); _makeTempFileName (str2, suffix); g_ControlPanel->loadModel (str2); 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"); else if (strstr (d_currLumpName, ".bmp")) sprintf (suffix, "%d%s", pos, ".bmp"); _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 (g_MDLViewer->getGlWindow ()->loadTexture (str2, pos)) { if (pos == TEXTURE_BACKGROUND) 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); }