
struct PACKHEADER {
char type[2]; // Signature of the packer. Must be 'PK'
BYTE version; // Version of the pack
DWORD offset; // Offset of the data from the beginning of the file
DWORD counter; // Number of record in the file
char autor[10]; // Autor of the pack
char program[15]; // Title of the program that use the pack
char fill[FILL_LENGTH]; // Fill to 64 bytes
};
The offset and the fill fields are used to reserve space for future
implementations of the packer.For the data we can use a record like this (followed by the data itself):
struct DATAHEADER {
char name[15]; // Name of the data
BYTE type; // Type of data
WORD counter; // Number of records
DWORD size; // Size of the data
};
With name we can identify the data informations in the file. This field can't be
duplicated! The type field identify the type of data that follow the record (
image, sound, game level, ..). The counter field contain the number of records registered
in the data. For have the size of one record, divide size by counter.insert algorithm:
// Insert general data information
BOOL pack_insert_data(char *filename, char *name, int size, void *data)
{
BOOL ret;
FILE *stream;
int position;
struct PACKHEADER packheader;
struct DATAHEADER dataheader;
// Open the file and check the signature
if((stream=fopen(filename, "rb+"))==NULL) return(PACK_OPEN);
if(fread(&packheader, sizeof(struct PACKHEADER), 1, stream)!=1) {
fclose(stream);
return(PACK_READ);
}
if(packheader.type[0]!='P'||packheader.type[1]!='K') {
fclose(stream);
return(PACK_INVALID);
}
// Look for records with the same name
if(strlen(name)>14) name[14]='\x0';
for(;;) {
position=filelength(fileno(stream));
if(fread(&dataheader, sizeof(struct DATAHEADER), 1, stream)!=1) break;
if(!strcmp(dataheader.name, name)) {
fclose(stream);
return(PACK_DUP);
}
// Ignore the data
fseek(stream, dataheader.size, SEEK_CUR);
}
fflush(stream);
fseek(stream, filelength(fileno(stream)), SEEK_SET);
// Fill the DATAHEADER structure and write it to the file
strncpy(dataheader.name, name, 14); dataheader.name[14]='\x0';
dataheader.type=TYPE_IMAGES;
dataheader.counter=0;
dataheader.size=size;
if(fwrite(&dataheader, sizeof(struct DATAHEADER), 1, stream)!=1) {
fclose(stream);
return(PACK_WRITE);
}
// Write the data
if(fwrite(data, size, 1, stream)!=1) {
fclose(stream);
return(PACK_WRITE);
}
// Update the packheader
packheader.counter++;
fseek(stream, 0, SEEK_SET);
if(fwrite(&packheader, sizeof(struct PACKHEADER), 1, stream)!=1) {
fclose(stream);
return(PACK_WRITE);
}
fclose(stream);
return(PACK_OK);
}
For deleting a record we should use a swap file where we copy all the records without the one that
we want to delete. Look the demo program for the code
implementation.