|
De/Crunching Class
Submitted by |
When writing code, sometimes I want to release a demo to public
appreciation,
but I don't want to expose some files (bitmaps, meshes, sounds, etc). I want
to
release a single executable file containing all required information, packed
in
a simple and fast way. The presented code do this, without much pain.
To me, it's specially useful when the app is to read a custom packed file
from disk
(a game level, for example). So the packed level can just be "crunched" into
the executable and read directly from there at run-time. Simple and clean.
Decruncher is a simple way of doing this trick. The idea is simple: I feed
the cruncher
with a list of files and an executable name. The cruncher put a signature, a
table of
contents (without directory information) and all the files in the list at
the end of the
specified executable.
Inside the app, I just ask to decrunch the table of contents, and start
reading from there,
using the offsets provided by the decruncher. Decruncher will NOT extract
the files from
the archive, it'll just tell where the files are stored in the executable
and their sizes.
NOTE: this is a one-way process, so to crunch again I need to regenerate the
original
executable (or copy over), as the cruncher just appends to the end of the
file. Also, there
is NO compression involved, I read back the file exactly as I do without the
crunching.
I know, there are other more "educated" ways of doing this, but old demo
coders
usually did it exactly this way, so here it is. For a quick demo release it
works fine.
Please read the sample included. The sample is a simple console utility that
crunchs a list of
files to the end of any other file then read back their offsets.
Regards,
Vander Nunes
|
Currently browsing [crunch.zip] (33,088 bytes) - [crunch.cpp] - (1,981 bytes)
//----------------------------------------------------------------------
// De/Cruncher 1.0
// Copyleft 1999, Vander Nunes
//
// This is free software, provided AS IS - use it at your own risk.
// If you use this code, a mention is appreciated but not required.
//
// This code is released as a technique sample, not meant to be an
// example of elegancy or portability.
//
// Module: crunch.cpp (sample application)
//----------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>
#include "decrunch.h"
//
// A simple example of use. This will read a list of files
// from a text file and crunch all files to the end of the
// specified target file. Then the list will be read again
// and listed.
//
int main(int argc, char *argv[], char *envp[])
{
printf("\n----------------------------------------------\n");
printf("CRUNCH 1.0\n(C)1999, Vander Nunes");
printf("\n----------------------------------------------\n");
if (argc != 3)
{
printf("sintax: crunch <filelist.txt> <targetfile>\n");
printf("sample: crunch bmps.txt demo.exe\n");
exit(0);
}
printf("crunching %s into %s...",argv[1],argv[2]);
CCrunch Cruncher;
if (!Cruncher.Crunch(argv[1],argv[2]))
printf("FAILED!\n");
else
{
printf("--------------------------------------------\n");
printf("Now testing\n");
DWORD Offset;
if (Cruncher.Decrunch(argv[2],&Offset))
{
printf("The list was found at offset %u.\n",Offset);
printf("There are %u files crunched.\n",Cruncher.EntryCount());
for (WORD n=0;n<Cruncher.EntryCount();n++)
{
printf("File :%s - ",Cruncher.Entry(n)->cName);
printf("Offset:%u - ",Cruncher.Entry(n)->dwOffset);
printf("Size :%u\n",Cruncher.Entry(n)->dwSize);
}
}
else
{
printf("Something bad happened while decrunching...\n");
}
}
return 0;
}
|
|
Currently browsing [crunch.zip] (33,088 bytes) - [decrunch.cpp] - (7,412 bytes)
//----------------------------------------------------------------------
// De/Cruncher 1.0
// Copyleft 1999, Vander Nunes
//
// This is free software, provided AS IS - use it at your own risk.
// If you use this code, a mention is appreciated but not required.
//
// This code is released as a technique sample, not meant to be an
// example of elegancy or portability.
//
// Module: decrunch.cpp
//----------------------------------------------------------------------
#include "stdio.h"
#include "decrunch.h"
CCrunch::CCrunch()
{
m_pEntries = NULL;
m_wEntryCount=0;
}
CCrunch::~CCrunch()
{
Reset();
}
//-------------------------------------------
// crunch a bunch of files listed in a txt file (ListFile)
// to OutFile
//-------------------------------------------
BOOL CCrunch::Crunch(char *ListFile,char *OutFile)
{
WORD n;
char Name[33];
FILE *fin, *fout;
//--------------------------------
// read the list of filenames
//--------------------------------
fin = fopen(ListFile, "rb");
if (fin==NULL) return FALSE;
m_wEntryCount = 0;
while(fscanf(fin,"%s",Name)!=EOF)
{
m_wEntryCount++;
if (m_pEntries==NULL)
m_pEntries = (CEntry**)malloc(m_wEntryCount*sizeof(CEntry*));
else
m_pEntries = (CEntry**)realloc(m_pEntries,m_wEntryCount*sizeof(m_pEntries));
m_pEntries[m_wEntryCount-1] = new CEntry();
memset(&m_pEntries[m_wEntryCount-1]->cName[0],0,33*sizeof(char));
strcpy(m_pEntries[m_wEntryCount-1]->cName,Name);
}
fclose(fin);
//--------------------------------
// find each file and get
// their size, calculating
// offsets
//--------------------------------
DWORD Offset = sizeof(WORD) + m_wEntryCount * 41;
HANDLE h;
WIN32_FIND_DATA Srch;
for (n=0; n<m_wEntryCount;n++)
{
h = FindFirstFile(m_pEntries[n]->cName,&Srch);
FindClose(h);
if (h == INVALID_HANDLE_VALUE)
{
// file not found - just fail the entire process!
Reset();
return FALSE;
}
m_pEntries[n]->dwOffset = Offset;
m_pEntries[n]->dwSize = Srch.nFileSizeHigh*MAXDWORD+Srch.nFileSizeLow;
Offset += m_pEntries[n]->dwSize;
}
//--------------------------------
// crunch all files to OutFile
//--------------------------------
fout = fopen(OutFile, "ab");
// write cruncher signature
char cSignature[] = CRUNCH_SIGNATURE;
// crypt to avoid signature duplication
// in the exe file.
for (n=0; n<strlen(cSignature); n++)
cSignature[n]+=1;
// write signature
fwrite(cSignature,strlen(cSignature),1,fout);
// write filecount
fwrite(&m_wEntryCount,sizeof(WORD),1,fout);
// write list
for (n=0; n<m_wEntryCount;n++)
{
fwrite(&m_pEntries[n]->cName[0],33*sizeof(char),1,fout);
fwrite(&m_pEntries[n]->dwOffset,sizeof(DWORD),1,fout);
fwrite(&m_pEntries[n]->dwSize,sizeof(DWORD),1,fout);
}
// write files
char *pBuffer;
DWORD dwRead,dwWrite;
DWORD dwTotalRead,dwTotalWrite, dwTotal;
pBuffer = (char*)malloc(BUFFER_SIZE*sizeof(char));
if (pBuffer==NULL)
{
fclose(fout);
Reset();
return FALSE;
}
printf("\n");
dwTotal = 0;
for (n=0; n<m_wEntryCount;n++)
{
printf("adding: %s ... ",m_pEntries[n]->cName);
fin = fopen(m_pEntries[n]->cName, "rb");
if (fin)
{
dwTotalRead = 0;
dwTotalWrite = 0;
do {
dwRead = fread(pBuffer,sizeof(char),BUFFER_SIZE,fin);
dwWrite = fwrite(pBuffer,sizeof(char),dwRead,fout);
dwTotalRead += dwRead;
dwTotalWrite += dwWrite;
} while (dwRead==BUFFER_SIZE);
//printf("%s: read %u written %u\n",m_pEntries[n].cName,dwTotalRead,dwTotalWrite);
dwTotal += dwTotalWrite;
fclose(fin);
printf("OK\n");
}
else
{
printf("ERROR!\n");
}
}
printf("%u files, total: %u bytes\n",m_wEntryCount,dwTotal);
fclose(fout);
Reset();
free(pBuffer);
return TRUE;
}
//-------------------------------------------
// return the offset of the list in a
// crunched file.
//-------------------------------------------
BOOL CCrunch::Decrunch(char *File, DWORD *dwOffset)
{
char *pBuffer;
BYTE p;
WORD c;
DWORD dwRead;
FILE *fin;
char cSignature[] = CRUNCH_SIGNATURE;
for (c=0; c<strlen(cSignature); c++)
cSignature[c] += 1;
fin = fopen(File, "rb");
if (fin==NULL) return FALSE;
// allocate buffer
pBuffer = (char*)malloc(BUFFER_SIZE*sizeof(char));
if (pBuffer==NULL)
{
fclose(fin);
return FALSE;
}
*dwOffset = 0;
p = 0;
do {
dwRead = fread(pBuffer,sizeof(char),BUFFER_SIZE,fin);
// search through buffer
for (WORD c=0; c<BUFFER_SIZE; c++)
{
if (pBuffer[c] == cSignature[p])
{
p++;
if (p == strlen(cSignature))
{
// signature found.
*dwOffset += (c+1);
goto readok;
}
} else p=0;
}
*dwOffset += dwRead;
} while (dwRead==BUFFER_SIZE);
readok:
fclose(fin);
free(pBuffer);
// read the file list
return ReadList(File,*dwOffset);
}
//---------------------------------------------------------------------------------------------
// reads the crunched files list from the source crunched file.
//
// list format:
//
// WORD FileCount; // number of crunched files in the list
//
// char FileName[33]; // name of crunched file
// DWORD Offset; // file offset (counting list size but not counting exe filesize)
// DWORD Size; // file size
// ...
// file
// file
// file
//---------------------------------------------------------------------------------------------
BOOL CCrunch::ReadList(char *SrcFile,DWORD ListOffset)
{
FILE *f;
WORD n;
// free any previously allocated list
Reset();
// try opening the file
f = fopen(SrcFile, "rb");
if (f == NULL) return FALSE;
// position over the list
if (ListOffset)
fseek(f, ListOffset, SEEK_SET);
// read files counting
if (fread(&m_wEntryCount, sizeof(WORD), 1, f) != 1)
{
fclose(f);
return FALSE;
}
// alloc memory for the entire list
m_pEntries = (CEntry**)malloc(m_wEntryCount*sizeof(CEntry));
if (m_pEntries==NULL)
{
fclose(f);
return FALSE;
}
// read entries
for (n=0;n<m_wEntryCount;n++)
{
m_pEntries[n] = new CEntry();
fread(&m_pEntries[n]->cName[0],sizeof(char),33,f);
fread(&m_pEntries[n]->dwOffset,sizeof(DWORD),1,f);
fread(&m_pEntries[n]->dwSize,sizeof(DWORD),1,f);
}
fclose(f);
return TRUE;
}
//-------------------------------------------
// return a pointer to a listed file entry
//-------------------------------------------
CEntry* CCrunch::FindEntry(char *File)
{
WORD n;
for (n=0;n<m_wEntryCount;n++)
if (!_stricmp(m_pEntries[n]->cName,File))
return m_pEntries[n];
return NULL;
}
//-------------------------------------------
// Free entries
//-------------------------------------------
void CCrunch::Reset(void)
{
if (m_pEntries)
{
for (WORD n=0;n<m_wEntryCount;n++)
delete m_pEntries[n];
free(m_pEntries);
m_pEntries = NULL;
}
m_wEntryCount = 0;
}
|
|
Currently browsing [crunch.zip] (33,088 bytes) - [decrunch.h] - (1,999 bytes)
//----------------------------------------------------------------------
// De/Cruncher 1.0
// Copyleft 1999, Vander Nunes
//
// This is free software, provided AS IS - use it at your own risk.
// If you use this code, a mention is appreciated but not required.
//
// This code is released as a technique sample, not meant to be an
// example of elegancy or portability.
//
// Module: decrunch.h
//----------------------------------------------------------------------
#ifndef _DECRUNCH_H_
#define _DECRUNCH_H_
// sorry pals
#include <windows.h>
// buffered file io
#define BUFFER_SIZE 16384
// signature is "=VRNDCRNCH=" (ASCII -1 for each letter).
// this was done to avoid duplication in exe file.
#define CRUNCH_SIGNATURE "<UQMCBQMBG<"
class CEntry
{
public:
CEntry()
{
memset(cName,0,33);
dwOffset = 0;
dwSize = 0;
}
char cName[33]; // name of crunched file
DWORD dwOffset; // file offset (counting list size but not counting exe filesize)
DWORD dwSize; // file size
};
class CCrunch
{
public:
CEntry **m_pEntries;
WORD m_wEntryCount;
CCrunch();
virtual ~CCrunch();
// used when crunching files to the executable
BOOL Crunch(char *ListFile, char *OutFile);
// initialize decruncher with a crunched file
// (this will NOT unpack, just read the file table)
BOOL Decrunch(char *File, DWORD *dwOffset);
// get the file count in the crunch
WORD EntryCount() { return m_wEntryCount; };
// get an entry from the list (name, offset and size)
CEntry* Entry(WORD wIndex) { if (wIndex >= m_wEntryCount) return NULL; else return m_pEntries[wIndex]; };
// clear entries
void Reset(void);
private:
BOOL ReadList(char *SrcFile, DWORD ListOffset);
CEntry* FindEntry(char *File);
};
#endif
|
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|