Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pk3.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2002-2003 LibPK3, Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in
12  * the documentation and/or other materials provided with the
13  * distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE LIBPK3 PROJECT
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
17  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
18  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE LIBPK3 PROJECT OR CONTRIBUTORS
20  * BE LIABLE FORANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27  * DAMAGE.
28  *
29  * The views and conclusions contained in the software and
30  * documentation are those of the authors and should not be
31  * interpreted as representing official policies, either expressed or
32  * implied, of the LibPK3 Project or Viktor Liehr alias picard.
33  *
34  * $Id: pk3.cpp 13333 2012-03-19 08:20:14Z pheonixstorm $
35  *
36  */
37 
38 /*
39  * Modified by Stephane Vaxelaire on 2003/09/23
40  * - Turned longs into ints in the headers (long are 8 bytes on some (all?) 64 bits archs)
41  * - Added endianness support (was not working on big endian machine)
42  */
43 
44 #include "pk3.h"
45 #include <cstdlib>
46 #include <iostream>
47 #include "posh.h"
48 using std::cerr;
49 using std::endl;
50 using std::hex;
51 
52 #pragma pack(2)
53 
55 {
56  enum
57  {
58  SIGNATURE=0x04034b50,
61  };
62  unsigned int sig;
63  unsigned short version;
64  unsigned short flag;
65  unsigned short compression; //COMP_xxxx
66  unsigned short modTime;
67  unsigned short modDate;
68  unsigned int crc32;
69  unsigned int cSize;
70  unsigned int ucSize;
71  unsigned short fnameLen; //Filename string follows header.
72  unsigned short xtraLen; //Extra field follows filename.
73 
75  {
76  sig = POSH_LittleU32( sig );
87  }
88 };
89 
91 {
92  enum
93  {
94  SIGNATURE=0x06054b50,
95  };
96  unsigned int sig;
97  unsigned short nDisk;
98  unsigned short nStartDisk;
99  unsigned short nDirEntries;
100  unsigned short totalDirEntries;
101  unsigned int dirSize;
102  unsigned int dirOffset;
103  unsigned short cmntLen;
104 
106  {
107  sig = POSH_LittleU32( sig );
115  }
116 };
117 
119 {
120  enum
121  {
122  SIGNATURE=0x02014b50,
125  };
126  unsigned int sig;
127  unsigned short verMade;
128  unsigned short verNeeded;
129  unsigned short flag;
130  unsigned short compression; //COMP_xxxx
131  unsigned short modTime;
132  unsigned short modDate;
133  unsigned int crc32;
134  unsigned int cSize; //Compressed size
135  unsigned int ucSize; //Uncompressed size
136  unsigned short fnameLen; //Filename string follows header.
137  unsigned short xtraLen; //Extra field follows filename.
138  unsigned short cmntLen; //Comment field follows extra field.
139  unsigned short diskStart;
140  unsigned short intAttr;
141  unsigned int extAttr;
142  unsigned int hdrOffset;
143 
144  char * GetName() const
145  {
146  return (char*) (this+1);
147  }
148  char * GetExtra() const
149  {
150  return GetName()+fnameLen;
151  }
152  char * GetComment() const
153  {
154  return GetExtra()+xtraLen;
155  }
156 
158  {
159  sig = POSH_LittleU32( sig );
162  flag = POSH_LittleU16( flag );
176  }
177 };
178 
179 #pragma pack()
180 
181 CPK3::CPK3( FILE *n_f )
182 {
183  CheckPK3( n_f );
184 }
185 
186 CPK3::CPK3( const char *filename )
187 {
188  Open( filename );
189 }
190 
191 static size_t bogus_sizet; //added by chuck_starchaser to squash some warnings
192 
193 bool CPK3::CheckPK3( FILE *f )
194 {
195  if (f == NULL)
196  return false;
197  TZipDirHeader dh;
198 
199  fseek( f, -(int) sizeof (dh), SEEK_END );
200 
201  long dhOffset = ftell( f );
202  memset( &dh, 0, sizeof (dh) );
203 
204  bogus_sizet = fread( &dh, sizeof (dh), 1, f );
205  dh.correctByteOrder();
206  //Check
207  if (dh.sig != TZipDirHeader::SIGNATURE) {
208  cerr<<"PK3 -- BAD DIR HEADER SIGNATURE, NOT A PK3 FILE !"<<endl;
209  exit( 1 );
210  return false;
211  }
212  //Go to the beginning of the directory.
213  fseek( f, dhOffset-dh.dirSize, SEEK_SET );
214 
215  //Allocate the data buffer, and read the whole thing.
216  m_pDirData = new char[dh.dirSize+dh.nDirEntries*sizeof (*m_papDir)];
217  if (!m_pDirData) {
218  cerr<<"PK3 -- ERROR ALLOCATING DATA BUFFER !"<<endl;
219  exit( 1 );
220  return false;
221  }
222  memset( m_pDirData, 0, dh.dirSize+dh.nDirEntries*sizeof (*m_papDir) );
223  bogus_sizet = fread( m_pDirData, dh.dirSize, 1, f );
224 
225  //Now process each entry.
226  char *pfh = m_pDirData;
227  m_papDir = (const TZipDirFileHeader**) (m_pDirData+dh.dirSize);
228 
229  bool ret = true;
230  for (int i = 0; i < dh.nDirEntries && ret == true; i++) {
231  TZipDirFileHeader &fh = *(TZipDirFileHeader*) pfh;
232  fh.correctByteOrder();
233 
234  //Store the address of nth file for quicker access.
235  m_papDir[i] = &fh;
236  //Check the directory entry integrity.
237  if (fh.sig != TZipDirFileHeader::SIGNATURE) {
238  cerr<<"PK3 -- ERROR BAD DIRECTORY SIGNATURE !"<<endl;
239  exit( 1 );
240  ret = false;
241  } else {
242  pfh += sizeof (fh);
243  //Convert UNIX slashes to DOS backlashes.
244  for (int j = 0; j < fh.fnameLen; j++)
245  if (pfh[j] == '/')
246  pfh[j] = '\\';
247  //Skip name, extra and comment fields.
248  pfh += fh.fnameLen+fh.xtraLen+fh.cmntLen;
249  }
250  }
251  if (ret != true) {
252  delete[] m_pDirData;
253  } else {
254  m_nEntries = dh.nDirEntries;
255  this->f = f;
256  }
257  return ret;
258 }
259 
260 bool CPK3::Open( const char *filename )
261 {
262  f = fopen( filename, "rb" );
263  if (f) {
264  strcpy( pk3filename, filename );
265  return CheckPK3( f );
266  } else {
267  return false;
268  }
269 }
270 
271 bool CPK3::ExtractFile( const char *lp_name )
272 {
273  return ExtractFile( lp_name, lp_name );
274 }
275 
276 bool CPK3::ExtractFile( const char *lp_name, const char *new_filename )
277 {
278  //open file tp write data
279  FILE *new_f = NULL;
280  int size = -1;
281 
282  char *data_content = ExtractFile( lp_name, &size );
283  if (data_content) {
284  if (size != -1) {
285  new_f = fopen( new_filename, "wb" );
286  fwrite( data_content, 1, size, new_f );
287  fclose( new_f );
288  delete data_content;
289  return true;
290  }
291  }
292  return false; //probably file not found
293 }
294 
295 //Compares 2 c-strings but do not take into account '/' or '\'
296 int vsstrcmp( const char *lp, const char *str )
297 {
298  unsigned int i, ok = 1;
299  unsigned int len = strlen( lp );
300  for (i = 0; ok && i < len; i++)
301  if (lp[i] != '/' && lp[i] != '\\' && lp[i] != str[i])
302  ok = 0;
303  return !ok;
304 }
305 
306 int CPK3::FileExists( const char *lpname )
307 {
308  char str[PK3LENGTH];
309  int idx = -1;
310 
311  memset( &str, 0, sizeof (str) );
312  for (int i = 0; idx == -1 && i < m_nEntries; i++) {
313  GetFilename( i, str );
314  int result = vsstrcmp( lpname, str );
315  if (result == 0) {
316  cerr<<"FOUND IN PK3 FILE : "<<lpname<<" with index="<<i<<endl;
317  idx = i;
318  }
319  }
320  //if the file isn't in the archive idx=-1
321  return idx;
322 }
323 
324 char* CPK3::ExtractFile( int index, int *file_size )
325 {
326  char *buffer;
327  int flength = GetFileLen( index );
328 
329  buffer = new char[flength];
330  if (!buffer) {
331  cerr<<"Unable to allocate memory, probably to low memory !!!"<<endl;
332  return NULL;
333  } else {
334  if ( true == ReadFile( index, buffer ) ) {
335  //everything went well !!!
336  } else {
337  cerr<<"\nThe file was found in the archive, but I was unable to extract it. Maybe the archive is broken."<<endl;
338  }
339  }
340  *file_size = flength;
341  return buffer;
342 }
343 
344 char* CPK3::ExtractFile( const char *lpname, int *file_size )
345 {
346  char str[PK3LENGTH];
347  int index = -1;
348  char *buffer;
349 
350  memset( &str, 0, sizeof (str) );
351  for (int i = 0; index == -1 && i < m_nEntries; i++) {
352  GetFilename( i, str );
353  int result = vsstrcmp( lpname, str );
354  if (result == 0)
355  index = i;
356  }
357  //if the file isn't in the archive
358  if (index == -1)
359  return false;
360  int flength = GetFileLen( index );
361 
362  buffer = new char[flength];
363  if (!buffer) {
364  printf( "Unable to allocate memory, probably to low memory !!!\n" );
365  return NULL;
366  } else {
367  if ( true == ReadFile( index, buffer ) ) {
368  //everything went well !!!
369  } else {
370  printf( "\nThe file was found in the archive, but I was unable to " \
371  "extract it. Maybe the archive is broken.\n" );
372  }
373  }
374  *file_size = flength;
375  return buffer;
376 }
377 
379 {
380  fclose( f );
381  delete[] m_pDirData;
382  m_nEntries = 0;
383 
384  return true;
385 }
386 
388 {
389  printf( "PK3 File: %s\n", pk3filename );
390  printf( "files count: %d\n\n", m_nEntries );
391  for (int i = 0; i < m_nEntries; i++) {}
392 }
393 
394 void CPK3::GetFilename( int i, char *pszDest ) const
395 {
396  if (pszDest != NULL) {
397  if (i < 0 || i >= m_nEntries) {
398  *pszDest = '\0';
399  } else {
400  memcpy( pszDest, m_papDir[i]->GetName(), m_papDir[i]->fnameLen );
401  pszDest[m_papDir[i]->fnameLen] = '\0';
402  }
403  }
404 }
405 
406 int CPK3::GetFileLen( int i ) const
407 {
408  if (i < 0 || i >= m_nEntries)
409  return -1;
410  else
411  return m_papDir[i]->ucSize;
412 }
413 
414 bool CPK3::ReadFile( int i, void *pBuf )
415 {
416  if (pBuf == NULL || i < 0 || i >= m_nEntries) {
417  cerr<<"PK3ERROR : ";
418  if (pBuf == NULL)
419  cerr<<" pBuf is NULL !!!"<<endl;
420  else if (i < 0)
421  cerr<<" Bad index < 0 !!!"<<endl;
422  else if (i >= m_nEntries)
423  cerr<<" Index TOO BIG !!!"<<endl;
424  return false;
425  }
426  //Quick'n dirty read, the whole file at once.
427  //Ungood if the ZIP has huge files inside
428 
429  //Go to the actual file and read the local header.
430  fseek( this->f, m_papDir[i]->hdrOffset, SEEK_SET );
431  TZipLocalHeader h;
432 
433  memset( &h, 0, sizeof (h) );
434  bogus_sizet = fread( &h, sizeof (h), 1, this->f );
435  h.correctByteOrder();
436  if (h.sig != TZipLocalHeader::SIGNATURE) {
437  cerr<<"PK3 - BAD LOCAL HEADER SIGNATURE !!!"<<endl;
438  return false;
439  }
440  //Skip extra fields
441  fseek( this->f, h.fnameLen+h.xtraLen, SEEK_CUR );
442  if (h.compression == TZipLocalHeader::COMP_STORE) {
443  //Simply read in raw stored data.
444  bogus_sizet = fread( pBuf, h.cSize, 1, this->f );
445  return true;
446  } else if (h.compression != TZipLocalHeader::COMP_DEFLAT) {
447  cerr<<"BAD Compression level, found="<<h.compression<<" - expected="<<TZipLocalHeader::COMP_DEFLAT<<endl;
448  return false;
449  }
450  //Alloc compressed data buffer and read the whole stream
451  char *pcData = new char[h.cSize];
452  if (!pcData) {
453  cerr<<"PK3ERROR : Could not allocate memory buffer for decompression"<<endl;
454  return false;
455  }
456  memset( pcData, 0, h.cSize );
457  bogus_sizet = fread( pcData, h.cSize, 1, this->f );
458 
459  bool ret = true;
460 
461  //Setup the inflate stream.
462  z_stream stream;
463  int err, err2;
464 
465  stream.next_in = (Bytef*) pcData;
466  stream.avail_in = (uInt) h.cSize;
467  stream.next_out = (Bytef*) pBuf;
468  stream.avail_out = h.ucSize;
469  stream.zalloc = (alloc_func) 0;
470  stream.zfree = (free_func) 0;
471 
472  //Perform inflation. wbits < 0 indicates no zlib header inside the data.
473  err = inflateInit2( &stream, -MAX_WBITS );
474  if (err == Z_OK) {
475  err = inflate( &stream, Z_FINISH );
476  if (err == Z_STREAM_END)
477  err = Z_OK;
478  else if (err == Z_NEED_DICT)
479  cerr<<"PK3ERROR : Needed a dictionary"<<endl;
480  else if (err == Z_DATA_ERROR)
481  cerr<<"PK3ERROR : Bad data buffer"<<endl;
482  else if (err == Z_STREAM_ERROR)
483  cerr<<"PK3ERROR : Bad parameter, stream error"<<endl;
484  err2 = inflateEnd( &stream );
485  if (err2 == Z_STREAM_ERROR)
486  cerr<<"PK3ERROR : Bad parameter, stream error"<<endl;
487  err2 = inflateEnd( &stream );
488  if (err2 == Z_STREAM_ERROR)
489  cerr<<"PK3ERROR : Bad parameter, stream error"<<endl;
490  } else {
491  if (err == Z_STREAM_ERROR)
492  cerr<<"PK3ERROR : Bad parameter, stream error"<<endl;
493  else if (err == Z_MEM_ERROR)
494  cerr<<"PK3ERROR : Memory error"<<endl;
495  }
496  if (err != Z_OK) {
497  cerr<<"PK3ERROR : Bad decompression return code"<<endl;
498  ret = false;
499  }
500  delete[] pcData;
501  return ret;
502 }
503