Descent3/czip/CZip.cpp

781 lines
16 KiB
C++
Raw Normal View History

2024-04-16 03:43:29 +00:00
/*
* $Logfile: /DescentIII/Main/czip/CZip.cpp $
* $Revision: 2 $
* $Date: 8/27/98 3:26p $
* $Author: Jeff $
*
* General CZip class functions
*
* $Log: /DescentIII/Main/czip/CZip.cpp $
*
* 2 8/27/98 3:26p Jeff
* intial creation
*
* 1 8/27/98 3:26p Jeff
*
* $NoKeywords: $
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "pserror.h"
#include "Macros.h"
#include "CZip.h"
CZip::CZip()
{
bfile = NULL;
file = NULL;
ok_to_raw_write = ok_to_raw_read = false;
}
CZip::~CZip()
{
}
// Opens an archive file for reading
// open_raw = if this is true, then the file MUST be of the raw compressed type. You cannot
// open an OCF by raw. If a raw type archive is opened, you may only use the read
// byte type functions (ReadRaw*())
// returns true on success
bool CZip::OpenInputArchive(char *filename,bool open_raw)
{
if(bfile)
return false;
m_bRawType = open_raw;
bfile = OpenInputBitFile(filename);
if(!bfile)
return false;
bool ret = false;
//read in the header based on what type of file it is
if(open_raw){
//it should be a raw compressed file
ubyte type;
if(ReadRawHeader(bfile->file,&type)){
ret = true;
m_iCompressionType = type;
PrepareDecompressor(bfile);
}
}else{
//it should be an OCF file
ubyte type;
tFileInfo temp;
if(ReadOCFHeader(bfile->file,&temp,&type)){
ret = true;
}
VFseek(bfile->file,0,SEEK_SET);
/*
tFileInfo *info;
int count = GetOCFFileList(bfile,&info);
if(count<=0 || !info)
printf("Unable to get filelist\n");
else{
for(int i=0;i<count;i++){
printf("%s = %d = %d\n",info[i].filename,info[i].compressed_size,info[i].expanded_size);
}
free(info);
}
*/
}
if(!ret && bfile!=NULL){
CloseInputBitFile(bfile);
bfile = NULL;
}
return ret;
}
// Opens an archive file for writing
// open_raw = if this is true, then the output archive will be of the raw compressed type.
// You must used the write byte type functions (WriteRaw*())
// compression_type = You only need to specify this if you opened raw
// returns true on success
bool CZip::OpenOutputArchive(char *filename,bool open_raw,int compression_type)
{
if(bfile)
return false;
bool ret = false;
bfile = OpenOutputBitFile(filename);
if(!bfile)
return false;
m_bRawType = open_raw;
m_iCompressionType = compression_type;
if(open_raw){
//write out header
if(WriteRawHeader(bfile->file,compression_type)){
//everything's ok
ret = true;
}
PrepareCompressor(bfile);
}else{
//can't do much here, since we don't know what file is going to be compressed yet
ret = true;
}
if(!ret && bfile!=NULL){
CloseInputBitFile(bfile);
bfile = NULL;
}
return ret;
}
// Reads in a Raw type file header
// returns true on succes, if so, compr_type contains the compression type
bool CZip::ReadRawHeader(tVirtualFile *file,ubyte *compr_type)
{
int magic_num;
if(!VFread(&magic_num,sizeof(int),1,file))
return false;
if(magic_num!=0x52415743)
return false;
//it looks good, get our compression type and scram
ubyte type;
if(!VFread(&type,1,1,bfile->file))
return false;
//valid compression type?
*compr_type = type;
return true;
}
// Writes out a Raw type file header
// returns true on succes
bool CZip::WriteRawHeader(tVirtualFile *file,ubyte compr_type)
{
int magic_num = 0x52415743;
//check compression type!?
if(!VFwrite(&magic_num,sizeof(int),1,file))
return false;
if(!VFwrite(&compr_type,1,1,file))
return false;
return true;
}
// Reads in an OCF header
// returns true on success, info is filled in the appropriate values,compr_type is compression type
bool CZip::ReadOCFHeader(tVirtualFile *file,tFileInfo *info,ubyte *compr_type)
{
int magic_num;
if(!VFread(&magic_num,sizeof(int),1,file))
return false;
if(magic_num!=0x4D495241)
return false;
//it looks good, its an OCF at least
ubyte version;
if(!VFread(&version,1,1,file))
return false;
if(version!=OCF_VERSION)
return false;
//version is corrent, fill in the rest
ubyte filename_size;
char buffer[300];
int lo_time,hi_time;
ubyte ctype;
int compressed_size,expand_size;
ubyte last_ditch_value;
if(!VFread(&filename_size,1,1,file))
return false;
if(filename_size<=0)
return false;
if(!VFread(buffer,1,filename_size,file))
return false;
buffer[filename_size] = '\0';
if(!VFread(&lo_time,sizeof(int),1,file))
return false;
if(!VFread(&hi_time,sizeof(int),1,file))
return false;
if(!VFread(&ctype,1,1,file))
return false;
if(!VFread(&compressed_size,sizeof(int),1,file))
return false;
if(!VFread(&expand_size,sizeof(int),1,file))
return false;
if(!VFread(&last_ditch_value,1,1,file))
return false;
if(last_ditch_value!=0x01)
return false;
//everything went ok, *whew*
*compr_type = ctype;
strcpy(info->filename,buffer);
info->lo_time = lo_time;
info->hi_time = hi_time;
info->compressed_size = compressed_size;
info->expanded_size = expand_size;
return true;
}
// Writes out an OCF header
// returns true on success
bool CZip::WriteOCFHeader(tVirtualFile *file,tFileInfo *info,ubyte compr_type,int header_pos)
{
int magic_num = 0x4D495241;
ubyte filename_size;
ubyte last_ditch_value;
ubyte version;
bool ret = false;
int filepos = VFtell(file); //save the position of the file restorfilepos:
VFseek(file,header_pos,SEEK_SET); //get to the begining of the header
if(!VFwrite(&magic_num,sizeof(int),1,file))
goto restorfilepos;
version = OCF_VERSION;
if(!VFwrite(&version,1,1,file))
goto restorfilepos;
//version is corrent, fill in the rest
filename_size = strlen(info->filename);
last_ditch_value = 0x01;
if(!VFwrite(&filename_size,1,1,file))
goto restorfilepos;
if(!VFwrite(info->filename,1,filename_size,file))
goto restorfilepos;
if(!VFwrite(&info->lo_time,sizeof(int),1,file))
goto restorfilepos;
if(!VFwrite(&info->hi_time,sizeof(int),1,file))
goto restorfilepos;
if(!VFwrite(&compr_type,1,1,file))
goto restorfilepos;
if(!VFwrite(&info->compressed_size,sizeof(int),1,file))
goto restorfilepos;
if(!VFwrite(&info->expanded_size,sizeof(int),1,file))
goto restorfilepos;
if(!VFwrite(&last_ditch_value,1,1,file))
goto restorfilepos;
ret = true;
restorfilepos:
VFseek(file,filepos,SEEK_SET); //restore the file pos
return ret;
}
// Writes out a 'dummy' OCF header, just give what the final filename is
// you must call this before you compress data, then when done, call the read WriteOCFHeader
bool CZip::WriteDummyOCFHeader(tVirtualFile *file,char *filename)
{
int itemp = 0;
ubyte btemp = 0;
if(!VFwrite(&itemp,sizeof(int),1,file))
return false;
if(!VFwrite(&btemp,1,1,file))
return false;
//version is corrent, fill in the rest
ubyte filename_size = strlen(filename);
if(!VFwrite(&filename_size,1,1,file))
return false;
if(!VFwrite(filename,1,filename_size,file))
return false;
if(!VFwrite(&itemp,sizeof(int),2,file))
return false;
if(!VFwrite(&btemp,1,1,file))
return false;
if(!VFwrite(&itemp,sizeof(int),2,file))
return false;
if(!VFwrite(&btemp,1,1,file))
return false;
return true;
}
// Prepares one of the decompressors for raw reading
void CZip::PrepareDecompressor(BITFILE *bfile)
{
switch(m_iCompressionType){
case COMPT_HUFFADAPT_0:
ha_PrepareDecompress();
break;
default:
Int3();
break;
};
}
// Prepares one of the compressors for raw reading
void CZip::PrepareCompressor(BITFILE *bfile)
{
switch(m_iCompressionType){
case COMPT_HUFFADAPT_0:
ha_PrepareCompress();
break;
default:
Int3();
break;
};
}
// Closes an archive that was opened for input
void CZip::CloseInputArchive(void)
{
if(m_bRawType){
switch(m_iCompressionType){
case COMPT_HUFFADAPT_0:
ha_CloseRawDecompress();
break;
default:
Int3();
break;
};
}
//any more work to do?
if(bfile)
CloseInputBitFile(bfile);
bfile = NULL;
}
// Closes an archive that was opened for output
void CZip::CloseOutputArchive(void)
{
//if we have an OCF file, write the real header out
if(!m_bRawType){
//write out an int of 0 (signifing EOF)
int fake_magic = 0;
VFseek(bfile->file,0,SEEK_END);
VFwrite(&fake_magic,sizeof(int),1,bfile->file);
}else{
//since we are writing a raw, we need to finish it up
switch(m_iCompressionType){
case COMPT_HUFFADAPT_0:
ha_CloseRawCompress(bfile);
break;
default:
Int3();
break;
};
}
if(bfile)
CloseOutputBitFile(bfile);
bfile = NULL;
}
// Adds a given file to the archive
// You may only add files to OCF files (you passed false for open_raw when you opened)
// compression_type = what type of compression you want to use
bool CZip::AddFileToArchive(char *filename,int compression_type)
{
if(!bfile)
return false;
if(m_bRawType)
return false;
file = VFopen(filename,"rb");
if(!file)
return false;
//Add to the end of the file, so we need to move to the end, back up sizeof(int) to remove
//header
VFseek(bfile->file,0-sizeof(int),SEEK_END);
int compressed_size = -1;
int header_start = VFtell(bfile->file);
switch(compression_type){
case COMPT_NONE:
case COMPT_SHANFANO:
VFclose(file);
return false;
break;
case COMPT_HUFFBAS:
WriteDummyOCFHeader(bfile->file,filename);
compressed_size = hb_CompressFile(file,bfile);
break;
case COMPT_HUFFADAPT_0:
WriteDummyOCFHeader(bfile->file,filename);
compressed_size = ha_CompressFile(file,bfile);
break;
default:
VFclose(file);
return false;
};
if(compressed_size==-1){
VFclose(file);
return false;
}
//fill in the info
struct _stat st;
strcpy(current_file.filename,filename);
_stat(filename,&st);
current_file.expanded_size = st.st_size;
VFclose(file);
current_file.compressed_size = compressed_size;
//TODO: Get FileTimes
current_file.lo_time = current_file.hi_time = 0;
m_iCompressionType = compression_type;
WriteOCFHeader(bfile->file,&current_file,m_iCompressionType,header_start);
return true;
}
// Extracts a file from an OCF archive
// filename = The filename specified within the archive
// destfilename = The output filename (what it should get extracted to) pass NULL if it
// is the same as the original filename
bool CZip::ExtractFileFromArchive(char *filename,char *destfilename)
{
if(!bfile)
return false;
if(m_bRawType)
return false;
file = VFopen((destfilename)?destfilename:filename,"wb");
if(!file)
return false;
int pos = FindFileInOCF(bfile->file,filename);
if(pos==-1)
return false;
VFseek(bfile->file,pos,SEEK_SET);
//read in header
ubyte compr_type;
if(!ReadOCFHeader(bfile->file,&current_file,&compr_type))
return false;
m_iCompressionType = compr_type;
switch(m_iCompressionType){
case COMPT_NONE:
case COMPT_SHANFANO:
VFclose(file);
return false;
break;
case COMPT_HUFFBAS:
hb_ExpandFile(bfile,file);
break;
case COMPT_HUFFADAPT_0:
ha_ExpandFile(bfile,file);
break;
default:
VFclose(file);
return false;
};
VFclose(file);
return true;
}
// Searches through the opened OCF looking for filename, returns the file position
// of the file if found, or -1 if not.
int CZip::FindFileInOCF(tVirtualFile *file,char *filename)
{
int old_pos = VFtell(file);
VFseek(file,0,SEEK_SET); //go to the beginning of the file
int magic_num,pos;
ubyte version,filename_size;
ubyte buffer[300];
bool done = false;
int compsize;
while(!done){
//make sure we have an OCF
pos = VFtell(file);
VFread(&magic_num,sizeof(int),1,file);
if(magic_num!=0x4D495241){
//we don't have an OCF, or end of file
VFseek(file,old_pos,SEEK_SET);
return -1;
}
VFread(&version,1,1,file);
VFread(&filename_size,1,1,file);
VFread(buffer,1,filename_size,file);
buffer[filename_size] = '\0';
if(!stricmp(filename,(char *)buffer)){
//we found it...back up a couple bytes
VFseek(file,old_pos,SEEK_SET);
return pos;
}
//no luck with this file, move to the header of the next
VFseek(file,9,SEEK_CUR); //get to the compressed size
VFread(&compsize,sizeof(int),1,file); //we need this
VFseek(file,5+compsize,SEEK_CUR); //move 5 bytes (rest of header) + data
}
VFseek(file,old_pos,SEEK_SET);
return -1; //couldn't find it
}
// Gets a list of files in the OCF, make sure you free up the array after use
// returns the number of files in the OCF
// 0 or -1 on error (-1 is out of memory)
int CZip::GetFileList(tFileInfo **info)
{
if(m_bRawType)
return 0;
if(!bfile)
return 0;
return GetOCFFileList(bfile,info);
}
// Gets a list of files in the OCF, make sure you free up the array after use
// returns the number of files in the OCF
// 0 or -1 on error (-1 is out of memory)
int CZip::GetOCFFileList(BITFILE *bfile,tFileInfo **info)
{
int count = 0;
tVirtualFile *file = bfile->file;
//first find out how many files there are
int old_pos = VFtell(file);
VFseek(file,0,SEEK_SET); //go to the beginning of the file
int magic_num,compsize;
ubyte filename_size;
bool done = false;
while(!done){
//make sure we have an OCF
VFread(&magic_num,sizeof(int),1,file);
if(magic_num!=0x4D495241){
//we don't have an OCF, or end of file
break;
}
VFseek(file,1,SEEK_CUR);
VFread(&filename_size,1,1,file);
VFseek(file,filename_size+9,SEEK_CUR);
VFread(&compsize,sizeof(int),1,file); //we need this
VFseek(file,5+compsize,SEEK_CUR); //move 5 bytes (rest of header) + data
count++;
}
if(!count){
VFseek(file,old_pos,SEEK_SET);
return 0;
}
*info = (tFileInfo *)malloc(sizeof(tFileInfo)*count);
if(!info){
VFseek(file,old_pos,SEEK_SET);
return -1;
}
tFileInfo *files;
files = *info;
//now go back through and get the files
VFseek(file,0,SEEK_SET);
for(int index=0;index<count;index++){
VFseek(file,5,SEEK_CUR); //skip magic num and version
VFread(&filename_size,1,1,file);
VFread(files[index].filename,1,filename_size,file);
files[index].filename[filename_size] = '\0';
VFread(&files[index].lo_time,sizeof(int),1,file);
VFread(&files[index].hi_time,sizeof(int),1,file);
VFseek(file,1,SEEK_CUR); //skip compression type
VFread(&files[index].compressed_size,sizeof(int),1,file);
VFread(&files[index].expanded_size,sizeof(int),1,file);
VFseek(file,files[index].compressed_size+1,SEEK_CUR); //skip data and last ditch
}
VFseek(file,old_pos,SEEK_SET);
return count;
}
int CZip::ReadBytes(char *data,int count)
{
if(!m_bRawType)
return 0;
int c = 0;
while(count){
data[c] = ReadRawByte();
c++;
count--;
}
return 1;
}
void CZip::WriteBytes(char *data,int count)
{
if(!m_bRawType)
return;
int c =0;
while(count){
WriteRawByte(data[c]);
c++;
count--;
}
}
ubyte CZip::ReadRawByte(void)
{
if(!m_bRawType)
return -1;
ubyte data = 0;
switch(m_iCompressionType){
case COMPT_HUFFADAPT_0:
if(!ha_ReadRawByte(&data,bfile))
data = 0;
break;
}
return data;
}
ushort CZip::ReadRawShort(void)
{
if(!m_bRawType)
return -1;
//need to read 2 bytes
ushort data = 0;
data = ReadRawByte();
data = (data<<8) | (ReadRawByte());
return data;
}
uint CZip::ReadRawInt(void)
{
if(!m_bRawType)
return -1;
//need to read 4 bytes
uint data = 0;
data = ReadRawByte();
data = (data<<8) | (ReadRawByte());
data = (data<<8) | (ReadRawByte());
data = (data<<8) | (ReadRawByte());
return data;
}
float CZip::ReadRawFloat(void)
{
if(!m_bRawType)
return -1.0f;
//need to read 4 bytes
int data = 0;
data = ReadRawByte();
data = (data<<8) | (ReadRawByte());
data = (data<<8) | (ReadRawByte());
data = (data<<8) | (ReadRawByte());
return *((float *)&data);
}
void CZip::WriteRawByte(ubyte value)
{
switch(m_iCompressionType){
case COMPT_HUFFADAPT_0:
ha_WriteRawByte(value,bfile);
break;
}
}
void CZip::WriteRawShort(ushort value)
{
if(!m_bRawType)
return;
//write 2 bytes
ubyte data;
data = ((value&0xFF00)>>8);
WriteRawByte(data);
data = (value&0xFF);
WriteRawByte(data);
}
void CZip::WriteRawInt(uint value)
{
if(!m_bRawType)
return;
//write 4 bytes
ubyte data;
data = ((value&0xFF000000)>>24);
WriteRawByte(data);
data = ((value&0xFF0000)>>16);
WriteRawByte(data);
data = ((value&0xFF00)>>8);
WriteRawByte(data);
data = (value&0xFF);
WriteRawByte(data);
}
void CZip::WriteRawFloat(float value)
{
if(!m_bRawType)
return;
//write 4 bytes
ubyte data;
uint v = 0;
v = *((int *)&value);
data = ((v&0xFF000000)>>24);
WriteRawByte(data);
data = ((v&0xFF0000)>>16);
WriteRawByte(data);
data = ((v&0xFF00)>>8);
WriteRawByte(data);
data = (v&0xFF);
WriteRawByte(data);
}
bool CZip::RawEOF(void)
{
if(!m_bRawType)
return true;
return !(ok_to_raw_read);
}