Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. //
  9. // Half-Life Model Viewer (c) 1999 by Mete Ciragan
  10. //
  11. // file: pakviewer.cpp
  12. // last modified: May 04 1999, Mete Ciragan
  13. // copyright: The programs and associated files contained in this
  14. // distribution were developed by Mete Ciragan. The programs
  15. // are not in the public domain, but they are freely
  16. // distributable without licensing fees. These programs are
  17. // provided without guarantee or warrantee expressed or
  18. // implied.
  19. //
  20. // version: 1.2
  21. //
  22. // email: [email protected]
  23. // web: http://www.swissquake.ch/chumbalum-soft/
  24. //
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <mxtk/mx.h>
  29. #include "pakviewer.h"
  30. #include "mdlviewer.h"
  31. // #include "GlWindow.h"
  32. #include "StudioModel.h"
  33. #include "ControlPanel.h"
  34. #include "FileAssociation.h"
  35. int
  36. pak_ExtractFile (const char *pakFile, const char *lumpName, char *outFile)
  37. {
  38. FILE *file = fopen (pakFile, "rb");
  39. if (!file)
  40. return 0;
  41. int ident, dirofs, dirlen;
  42. fread (&ident, sizeof (int), 1, file);
  43. if (ident != (int) (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P'))
  44. {
  45. fclose (file);
  46. return 0;
  47. }
  48. fread (&dirofs, sizeof (int), 1, file);
  49. fread (&dirlen, sizeof (int), 1, file);
  50. fseek (file, dirofs, SEEK_SET);
  51. int numLumps = dirlen / 64;
  52. for (int i = 0; i < numLumps; i++)
  53. {
  54. char name[56];
  55. int filepos, filelen;
  56. fread (name, 56, 1, file);
  57. fread (&filepos, sizeof (int), 1, file);
  58. fread (&filelen, sizeof (int), 1, file);
  59. if (!mx_strcasecmp (name, lumpName))
  60. {
  61. FILE *out = fopen (outFile, "wb");
  62. if (!out)
  63. {
  64. fclose (file);
  65. return 0;
  66. }
  67. fseek (file, filepos, SEEK_SET);
  68. while (filelen--)
  69. fputc (fgetc (file), out);
  70. fclose (out);
  71. fclose (file);
  72. return 1;
  73. }
  74. }
  75. fclose (file);
  76. return 0;
  77. }
  78. PAKViewer::PAKViewer (mxWindow *window)
  79. : mxWindow (window, 0, 0, 0, 0, "", mxWindow::Normal)
  80. {
  81. strcpy (d_pakFile, "");
  82. strcpy (d_currLumpName, "");
  83. tvPAK = new mxTreeView (this, 0, 0, 0, 0, IDC_PAKVIEWER);
  84. pmMenu = new mxPopupMenu ();
  85. pmMenu->add ("Load Model", 1);
  86. pmMenu->addSeparator ();
  87. pmMenu->add ("Load Background", 2);
  88. pmMenu->add ("Load Ground", 3);
  89. pmMenu->addSeparator ();
  90. pmMenu->add ("Play Sound", 4);
  91. pmMenu->addSeparator ();
  92. pmMenu->add ("Extract File...", 5);
  93. setLoadEntirePAK (true);
  94. setVisible (false);
  95. }
  96. PAKViewer::~PAKViewer ()
  97. {
  98. //tvPAK->remove (0);
  99. //tvPAK->remove();
  100. closePAKFile ();
  101. }
  102. void
  103. _makeTempFileName (char *str, const char *suffix)
  104. {
  105. strcpy (str, mx_gettemppath ());
  106. strcat (str, "/hltempmodel");
  107. strcat (str, suffix);
  108. }
  109. int
  110. PAKViewer::handleEvent (mxEvent *event)
  111. {
  112. switch (event->event)
  113. {
  114. case mxEvent::Action:
  115. {
  116. switch (event->action)
  117. {
  118. case IDC_PAKVIEWER: // tvPAK
  119. if (event->flags & mxEvent::RightClicked)
  120. {
  121. pmMenu->setEnabled (1, strstr (d_currLumpName, ".mdl") != 0);
  122. pmMenu->setEnabled (2, strstr (d_currLumpName, ".tga") != 0);
  123. pmMenu->setEnabled (3, strstr (d_currLumpName, ".tga") != 0);
  124. pmMenu->setEnabled (4, strstr (d_currLumpName, ".wav") != 0);
  125. int ret = pmMenu->popup (tvPAK, event->x, event->y);
  126. switch (ret)
  127. {
  128. case 1:
  129. OnLoadModel ();
  130. break;
  131. case 2:
  132. OnLoadTexture (0);
  133. break;
  134. case 3:
  135. OnLoadTexture (1);
  136. break;
  137. case 4:
  138. OnPlaySound ();
  139. break;
  140. case 5:
  141. OnExtract ();
  142. break;
  143. }
  144. }
  145. else if (event->flags & mxEvent::DoubleClicked)
  146. {
  147. OnPAKViewer ();
  148. char e[16];
  149. strncpy (e, mx_getextension (d_currLumpName), 16);
  150. int mode = g_FileAssociation->getMode (&e[1]);
  151. if (mode == -1)
  152. return 1;
  153. char *program = g_FileAssociation->getProgram (&e[1]);
  154. #ifdef WIN32
  155. if (mode == 0)
  156. {
  157. char str[256];
  158. _makeTempFileName (str, e);
  159. if (!pak_ExtractFile (d_pakFile, d_currLumpName, str))
  160. mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  161. else
  162. {
  163. if (program)
  164. {
  165. char path[256];
  166. strcpy (path, program);
  167. strcat (path, " ");
  168. strcat (path, str);
  169. if ((int) WinExec (path, SW_SHOW) <= 32)
  170. mxMessageBox (this, "Error executing specified program.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  171. }
  172. }
  173. }
  174. // associated program
  175. else if (mode == 1)
  176. {
  177. char str[256];
  178. _makeTempFileName (str, e);
  179. if (!pak_ExtractFile (d_pakFile, d_currLumpName, str))
  180. mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  181. else
  182. if ((int) ShellExecute ((HWND) getHandle (), "open", str, 0, 0, SW_SHOW) <= 32)
  183. mxMessageBox (this, "Error executing document with associated program.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  184. }
  185. // HLMV default
  186. else
  187. #endif
  188. if (mode == 2)
  189. {
  190. if (!strcmp (e, ".mdl"))
  191. OnLoadModel ();
  192. else if (!strcmp (e, ".tga"))
  193. OnLoadTexture (0);
  194. else if (!strcmp (e, ".wav"))
  195. OnPlaySound ();
  196. return 1;
  197. }
  198. }
  199. return OnPAKViewer ();
  200. } // event->action
  201. } // mxEvent::Action
  202. break;
  203. case mxEvent::Size:
  204. {
  205. tvPAK->setBounds (0, 0, event->width, event->height);
  206. } // mxEvent::Size
  207. break;
  208. } // event->event
  209. return 1;
  210. }
  211. int
  212. PAKViewer::OnPAKViewer ()
  213. {
  214. mxTreeViewItem *tvi = tvPAK->getSelectedItem ();
  215. if (tvi)
  216. {
  217. strcpy (d_currLumpName, tvPAK->getLabel (tvi));
  218. // find the full lump name
  219. mxTreeViewItem *tviParent = tvPAK->getParent (tvi);
  220. char tmp[128];
  221. while (tviParent)
  222. {
  223. strcpy (tmp, d_currLumpName);
  224. strcpy (d_currLumpName, tvPAK->getLabel (tviParent));
  225. strcat (d_currLumpName, "/");
  226. strcat (d_currLumpName, tmp);
  227. tviParent = tvPAK->getParent (tviParent);
  228. }
  229. if (!d_loadEntirePAK)
  230. {
  231. // finally insert "models/"
  232. strcpy (tmp, d_currLumpName);
  233. strcpy (d_currLumpName, "models/");
  234. strcat (d_currLumpName, tmp);
  235. }
  236. }
  237. return 1;
  238. }
  239. int PAKViewer::OnLoadModel ()
  240. {
  241. static char str2[256];
  242. char suffix[16];
  243. strcpy (suffix, ".mdl");
  244. _makeTempFileName (str2, suffix);
  245. if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
  246. {
  247. mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  248. return 1;
  249. }
  250. g_pStudioModel->FreeModel ( false );
  251. if( !g_pStudioModel->LoadModel (str2) )
  252. {
  253. mxMessageBox (this, "Error reading model header.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  254. return 1;
  255. }
  256. return 1;
  257. }
  258. int PAKViewer::OnLoadTexture (int pos)
  259. {
  260. static char str2[256];
  261. char suffix[16] = "";
  262. if (strstr (d_currLumpName, ".tga"))
  263. sprintf (suffix, "%d%s", pos, ".tga");
  264. _makeTempFileName (str2, suffix);
  265. if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
  266. {
  267. mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  268. return 1;
  269. }
  270. if (0 /* g_MDLViewer->getGlWindow ()->loadTexture (str2, pos) */)
  271. {
  272. if (pos == 0)
  273. g_ControlPanel->setShowBackground (true);
  274. else
  275. g_ControlPanel->setShowGround (true);
  276. }
  277. else
  278. mxMessageBox (this, "Error loading texture.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  279. return 1;
  280. }
  281. int
  282. PAKViewer::OnPlaySound ()
  283. {
  284. #ifdef WIN32
  285. static char str2[256];
  286. char suffix[16] = "";
  287. // stop any playing sound
  288. PlaySound (0, 0, SND_FILENAME | SND_ASYNC);
  289. if (strstr (d_currLumpName, ".wav"))
  290. sprintf (suffix, "%d%s", 44, ".wav");
  291. _makeTempFileName (str2, suffix);
  292. if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
  293. {
  294. mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  295. return 1;
  296. }
  297. PlaySound (str2, 0, SND_FILENAME | SND_ASYNC);
  298. #endif
  299. return 1;
  300. }
  301. int
  302. PAKViewer::OnExtract ()
  303. {
  304. char *ptr = (char *) mxGetSaveFileName (this, "", "*.*");
  305. if (ptr)
  306. {
  307. if (!pak_ExtractFile (d_pakFile, d_currLumpName, ptr))
  308. mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  309. }
  310. return 1;
  311. }
  312. int
  313. _compare(const void *arg1, const void *arg2)
  314. {
  315. if (strchr ((char *) arg1, '/') && !strchr ((char *) arg2, '/'))
  316. return -1;
  317. else if (!strchr ((char *) arg1, '/') && strchr ((char *) arg2, '/'))
  318. return 1;
  319. else
  320. return strcmp ((char *) arg1, (char *) arg2);
  321. }
  322. bool
  323. PAKViewer::openPAKFile (const char *pakFile)
  324. {
  325. FILE *file = fopen (pakFile, "rb");
  326. if (!file)
  327. return false;
  328. int ident, dirofs, dirlen;
  329. // check for id
  330. fread (&ident, sizeof (int), 1, file);
  331. if (ident != (int) (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P'))
  332. {
  333. fclose (file);
  334. return false;
  335. }
  336. // load lumps
  337. fread (&dirofs, sizeof (int), 1, file);
  338. fread (&dirlen, sizeof (int), 1, file);
  339. int numLumps = dirlen / 64;
  340. fseek (file, dirofs, SEEK_SET);
  341. lump_t *lumps = new lump_t[numLumps];
  342. if (!lumps)
  343. {
  344. fclose (file);
  345. return false;
  346. }
  347. fread (lumps, sizeof (lump_t), numLumps, file);
  348. fclose (file);
  349. qsort (lumps, numLumps, sizeof (lump_t), _compare);
  350. // save pakFile for later
  351. strcpy (d_pakFile, pakFile);
  352. tvPAK->remove (0);
  353. char namestack[32][32];
  354. mxTreeViewItem *tvistack[32];
  355. for (int k = 0; k < 32; k++)
  356. {
  357. strcpy (namestack[k], "");
  358. tvistack[k] = 0;
  359. }
  360. for (int i = 0; i < numLumps; i++)
  361. {
  362. if (d_loadEntirePAK || !strncmp (lumps[i].name, "models", 6))
  363. {
  364. char *tok;
  365. if (d_loadEntirePAK)
  366. tok = &lumps[i].name[0];
  367. else
  368. tok = &lumps[i].name[7];
  369. int i = 1;
  370. while (tok)
  371. {
  372. char *end = strchr (tok, '/');
  373. if (end)
  374. *end = '\0';
  375. if (strcmp (namestack[i], tok))
  376. {
  377. strcpy (namestack[i], tok);
  378. /*
  379. if (i == 0)
  380. tvistack[i] = tvPAK->add (0, tok);
  381. else*/
  382. tvistack[i] = tvPAK->add (tvistack[i - 1], tok);
  383. for (int j = i + 1; j < 32; j++)
  384. {
  385. strcpy (namestack[j], "");
  386. tvistack[j] = 0;
  387. }
  388. }
  389. ++i;
  390. if (end)
  391. tok = end + 1;
  392. else
  393. tok = 0;
  394. }
  395. }
  396. }
  397. delete[] lumps;
  398. setVisible (true);
  399. return true;
  400. }
  401. void
  402. PAKViewer::closePAKFile ()
  403. {
  404. strcpy (d_pakFile, "");
  405. setVisible (false);
  406. }