Skip to main content
gamedev.net
🔒 Locked

Extracting files

Started by Kryptus 7 replies 550+ views
Original Post
Kryptus
Kryptus
I have written code to open and show the contents of a Quake 2 PAK file into a listview control. I can show them but what I want to know is how can I extract the contents of the first file? Below is the code and my attempt at trying to figure out the extract code. Any help is appreciated!

typedef struct
{
	char Signature[4];
	int DirOffset;
	int DirLength;
} pakHeader;

typedef struct
{
	char FileName[56];
	int FilePosition;
	int FileLength;
} pakEntry;

FILE *file;
pakHeader Header;
pakEntry *Entry;
int NumFiles;

bool OpenPAK(char FileName[])
{
	// Open a PAK file in binary mode for reading
	file = fopen(FileName, "rb");

	fread(&Header, sizeof(Header), 1, file);
	if (strncmp(Header.Signature, "PACK", 4))
		fclose(file);

	NumFiles = (Header.DirLength / 64);
	Entry = new pakEntry[NumFiles];

	fseek(file, Header.DirOffset, SEEK_SET);
	fread(Entry, sizeof(pakEntry), NumFiles, file);

	for (int i = 0; i < NumFiles; i++)
	{
		lvItem.iItem = 0;
		lvItem.iSubItem = 0;
		lvItem.pszText = Entry.FileName;
		SendMessage(hListView, LVM_INSERTITEM, 0, (LPARAM) &lvItem);

		lvItem.iItem = 0;
		lvItem.iSubItem = 3;
		lvItem.pszText = (char *) Entry.FileLength;
		SendMessage(hListView, LVM_SETITEMTEXT, 0, (LPARAM) &lvItem);
	}

	return true;
}

void ExtractPAK(char FileName[])
{
	int *ByteArray;
	ByteArray = new int[Entry[1].FileLength];

	fseek(file, Entry[1].FilePosition, SEEK_SET);
	fread(&ByteArray, sizeof(ByteArray), 1, file);

	file = fopen(FileName, "wb");

	fwrite(&ByteArray, sizeof(ByteArray), 1, file);
}

Spudder
Spudder
Instead of using an int array to store the data being read in try using a void* instead - you can use malloc() to allocate the memory or alternative you could use an unsigned char array.

Your also going to need to store details of each entry in the PAK file - a vector of pakEntry's will do the job. You'll then want to loop through NumEntries - 1 times and compare the filename passed in against the value in the vector at the current index. If the stings match seek to the files offset and read in the required amount of data, then you can write it back out to disk.
Kryptus
Kryptus
When I extract a file with this code, it saves only 4 characters. I must be overlooking something...
Spudder
Spudder
Here's the Read() method from my own reader:

int CQuake2Pak::ReadFile(const string &FileName, void **FileBuffer, unsigned long &FileSize){  //make sure the file has previously been opened  if(!m_PakFile)    return Q2P_FILE_NOT_OPEN;  //locate the file in the entries list  for(unsigned int i = 0; i < m_NumEntries; i++)  {    if(FileName == m_PakEntries.m_FileName)    {      fseek(m_PakFile, m_PakEntries.m_FilePos, SEEK_SET);      //setup file buffer      *FileBuffer = malloc(m_PakEntries.m_FileLength);      if(!*FileBuffer)       return Q2P_OUT_OF_MEMORY;      //read the file in      if(fread(*FileBuffer, m_PakEntries.m_FileLength, 1, m_PakFile) != 1)        return Q2P_READ_ERROR;      else      {        FileSize = m_PakEntries.m_FileLength;	return Q2P_SUCCESS;      }    }  }  //file not found  return Q2P_FILE_NOT_FOUND;}


m_PakEntries is a vector of pakEntry objects which is created when the PAK is opened, m_PakFile is a FILE* handle to the PAK file.

The return values are members of an enum containing different return codes.

Im not entirely sure but the reason why your code is only writing four bytes may be due to sizeof(ByteArray) returning the size of the pointer (4 bytes) and not the actual amount of memory allocated, this can be solved using an unsigned char array then using the length of the file as the value when allocating the memory and writing the data
jeroenb
jeroenb
I could be wrong, as I haven't tried this out for a very long time, but doesn't sizeof ( ByteArray ) return 4 as it is a pointer? You better just use the file length field of the structure as you have it at hand.

JeroenB
Crafter 2D: the open source 2D game framework ?Github: https://github.com/crafter2d/crafter2d
Twitter: [twitter]crafter_2d[/twitter]
Kryptus
Kryptus
Thanks for that, but how can I do this? Does it remain a pointer or do I change it to something else? If I have to change it to something else then the other code will stuff up won't it?
Spudder
Spudder
The problem you have is with telling fwrite() how many bytes to write out, you already know how many bytes to write as it's one of the fields in pakEntry so you need to change the call to fwrite to something like this:

fwrite((void*)&ByteArray, Entry[1].FileLength, 1, file);


This will ensure that your writing the correct number of bytes out to to the file.
Kryptus
Kryptus
Thanks for that code but it only half worked. There were many more bytes in the file but they are unreadable. The file format I extracted was a text file so I should be able to read it when it has been extracted. Instead I can only see scrambled characters.
Kryptus
Kryptus
Well before I extract the file, I should be writing to ascii and not binary correct?

So I should remove the "b" ?

file = fopen(FileName, "wb");fwrite(&ByteArray, sizeof(ByteArray), 1, file);


and make it:

file = fopen(FileName, "w");fwrite(&ByteArray, sizeof(ByteArray), 1, file);


hmmm, still doesn't work properly...

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.