// vaupaklib.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"
#include "VAUPAKLIB.h"
#include <string.h>
using namespace std;

extern "C" _declspec(dllexport) void pak_create(CHAR *filename) //Create a new PAK File.
{

  pakfile mypak;
   
  mypak.header.head[0] = 'P';
  mypak.header.head[1] = 'A';
  mypak.header.head[2] = 'C';
  mypak.header.head[3] = 'K';
  
  mypak.header.dir_offset = sizeof(mypak.header.head) + 1;
  mypak.header.dir_length = 1;

  mypak.handle = fopen(filename, "wb");

  fwrite(&mypak.header,sizeof(mypak.header),1,mypak.handle);
  
  fclose(mypak.handle);
  
}

pakfile *pak_open (CHAR *filename)
{
  pakfile *mypak;
  FILE *fp;

    
  fp = fopen(filename, "r+");
    

  mypak = (pakfile *)malloc(sizeof(pakfile));
  mypak->handle = fp;

  
  
  if(mypak->handle == NULL)
     return NULL;

    
     fread(&mypak->header, sizeof(pakheader), 1, mypak->handle);
    
  return mypak;
}

extern "C" _declspec(dllexport) int pak_checktype(CHAR *pakname)
{
	pakfile *fp;
	fp = pak_open(pakname);
  if( fp->header.head[0] != 'P' || fp->header.head[1] != 'A' || fp->header.head[2] != 'C' || fp->header.head[3] != 'K')
    return -1;  //ident corrupt

  if( fp->header.dir_offset < (sizeof(fp->header.head) + 1) || fp->header.dir_length < 1)
    return -2;  //header corrupt

    
  return 0;
}

byte *readFile(CHAR *filename, unsigned int *size)
{
    FILE *fil;
    byte c;
	byte *buffer;
    int b;
    unsigned int count =0;

    
    fil = fopen(filename, "r");
    
    
    if( fil == NULL )
      return NULL;

    while(fread(  &c, sizeof(c), 1, fil) > 0)
      count++;
    
    //now we have the size of the file
    buffer = (byte *)malloc(count * sizeof(byte));
    
    rewind(fil);
    count = 0;
    //now read the data into the buffer
    while((b = fgetc(fil)) != EOF)
      {
        buffer[count] = b;
        count++;
      }
     
    fclose(fil);
   *size = count;
  return buffer;
}

extern "C" _declspec(dllexport) int pak_addfiles (CHAR *file, CHAR *inpak, CHAR *pakname)
{
	pakfile *fp;
	fp = pak_open(pakname);
    unsigned int count;
    dirsection sect;
    dirsection *files;
    int x =0;
    byte *buffer;
    if(sizeof(inpak) > 56) //to stay compatible to Quake 2 format, this may not be longer than 56 char
      return -1;

                  
    buffer = readFile(file, &count);
       
    if(fp->header.dir_length == 1)
       {
         fseek(fp->handle, 4, SEEK_SET);  //go to directory offset-record
         fp->header.dir_offset = sizeof(fp->header) + sizeof(byte)*count;
         fp->header.dir_length = sizeof(dirsection);
         //write new offset and length
         fwrite(&fp->header.dir_offset, sizeof(fp->header.dir_offset), 1, fp->handle);
         fwrite(&fp->header.dir_length, sizeof(fp->header.dir_length), 1, fp->handle);

 
         //now write the raw binary data
         fwrite(buffer, sizeof(byte), count, fp->handle);

            
         strcpy(sect.filename, inpak);
         sect.file_position = 12;
         sect.file_length = sizeof(byte) * count;
         
         //now write the dirsection
         fwrite(&sect, sizeof(sect), 1, fp->handle);
       }
       else
       {
         //first check if the file already exists in the pak
         //for this purpose we read all the dirsections into a buffer and then check them


         if( fp->header.dir_length % 64 != 0 )  // corrupted file ?
              return -1;                      // oh my gosh, check yer HD !

         //now check how many files are in the pak
         x = fp->header.dir_length / 64;  //guaranteed to be even

         //allocate space for all the files including one new
         files = (dirsection *)malloc((x+1) * sizeof(dirsection));

         //read all the dirsections into 'files'
         fseek(fp->handle, fp->header.dir_offset, SEEK_SET);  //go to directory offset

         fread(files, sizeof(dirsection), x, fp->handle);

         //now check if the file exists
         int i;
         for( i = 0; i < x; i++)
         {

           char *name = files[i].filename;

           if( strcmp(name, inpak) == 0 )
              return -1;  // already exists
         }

         //now calculate the new offset
         fseek(fp->handle, 4, SEEK_SET);  //go to directory offset-record
         fp->header.dir_offset = fp->header.dir_offset + count;
         fp->header.dir_length = fp->header.dir_length + sizeof(dirsection);
         //write new offset and length
         fwrite(&fp->header.dir_offset, sizeof(fp->header.dir_offset), 1, fp->handle);
         fwrite(&fp->header.dir_length, sizeof(fp->header.dir_length), 1, fp->handle);

         //now go to end of binary data and write the file
         fseek(fp->handle, (fp->header.dir_offset - count), SEEK_SET);

         fwrite(buffer, sizeof(byte), count, fp->handle);

         strcpy(files[x].filename, inpak);
        
         files[x].file_position = fp->header.dir_offset - count;
         files[x].file_length = sizeof(byte) * count;

         //now write the entire dirsection, since the old one has been overwritten by binary data
         fwrite(files, sizeof(dirsection), x+1, fp->handle);
                 
       }                     

       
       free(buffer);
pak_close(fp);
return 0;
}

extern "C" _declspec(dllexport) int pak_close(pakfile *fp)
{
  fclose(fp->handle);
  free(fp);
  return 0;
}

extern "C" _declspec(dllexport) dirsection *pak_FileExist(CHAR *file,CHAR *pakname)
{
    pakfile *fp;
	fp = pak_open(pakname);
   dirsection *sect;
         sect = (dirsection *)malloc(sizeof(dirsection));
                   
         int x =0;

         dirsection *files = loadDirSection(fp, &x);
                   
         //now check if the file exists
         int i;
         for( i = 0; i < x; i++)
         {

           char *name = files[i].filename;
           unsigned int position = files[i].file_position;
           unsigned int length = files[i].file_length;
           
           if( strcmp(name, file) == 0 )
              {
                strcpy(sect->filename, name);
                sect->file_position = position;
                sect->file_length = length;

                free(files);return sect;}  // exists
         }

         free(files);
         return NULL; // does not exist
pak_close(fp);
}

extern "C" _declspec(dllexport) int pak_extract (CHAR *file, CHAR *output,CHAR *pakname)
{
	pakfile *fp;
	fp = pak_open(pakname);
  FILE *fil;
  byte *buffer;
  if(fp == NULL)
	  return -1;
  //first, test if the file exists in the pak
  dirsection *mysect = pak_FileExist(file, pakname);

  if( mysect == NULL )
      return -1;   //not found
      
      //open the output file
      fil = fopen(output, "wb");

      //now go to the address in the pak
      fseek(fp->handle, mysect->file_position, SEEK_SET);
           
      buffer = (byte *)malloc(mysect->file_length * sizeof(byte));

    
      //now read the data into the buffer
      fread(buffer, sizeof(byte), mysect->file_length, fp->handle);

            
      //now write the buffer to the output file
      fwrite(buffer, sizeof(byte), mysect->file_length, fil);
      fclose(fil);
      free(buffer);
      
pak_close(fp); 
return 0;
}

extern "C" _declspec(dllexport) dirsection *loadDirSection(pakfile *fp, int *x)
{
      dirsection *files;
         

         //see how many files are in the pak
         *x = fp->header.dir_length / 64;  //guaranteed to be even

         //allocate space for all the files including one new (for possible insertion)
         files =(dirsection *)malloc(*x * sizeof(dirsection));

         //read all the dirsections into 'files'
         fseek(fp->handle, fp->header.dir_offset, SEEK_SET);  //go to directory offset

         fread(files, sizeof(dirsection), *x, fp->handle);

       return files;
}

extern "C" _declspec(dllexport) void pak_GetDirTree(pakfile *fp)
{
  int x;
  string tree;
	
  if(fp == NULL) { return ;}
	
  dirsection *myfiles = loadDirSection(fp, &x);
  
  string name;
  string size;
  x--;
  while( x >= 0)
  {
   name = "File:" + (string)myfiles[x].filename;
   size = "Size:" + myfiles[x].file_length;
/*printf("%25s", name);                  //give out the pathname of the file in pak
  printf("\t\t"); 
  printf("%8i", myfiles[x].file_length); //give out the size in bytes
  printf("  Bytes\n");*/
  tree += name + size + "\n";
  x--;
  }
}

