#include "stdafx.h"
#include "VirtualFileSystem.h"
VirtualFileSystem::VirtualFileSystem(string path)
{
//save important data
this->Exists = true;
this->LZMAFilePath = path;
//try to open the file, create it if it doesn't exists
FILE* RawData = fopen(this->LZMAFilePath.c_str(), "rb");
if (RawData == (FILE*)NULL)
{
this->Exists = false;
RawData = fopen(this->LZMAFilePath.c_str(), "w");
fclose(RawData);
RawData = fopen(this->LZMAFilePath.c_str(), "rb");
}
//if the file does exists
if (this->Exists)
{
//get its length
fseek(RawData, 0, SEEK_END);
size_t fileLength = ftell(RawData);
rewind(RawData);
//check the file length
if (fileLength > 0)
{
do {
//check if the header is corrupted
if ((fileLength - ftell(RawData)) > sizeof(LZMAFileHeader))
{
//save the position inside the file of the current header
this->PositionOfFiles.emplace_back(ftell(RawData));
//get the header of the file
LZMAFileHeader currentHeader;
fread(¤tHeader, sizeof(LZMAFileHeader), 1, RawData);
//create the space used to hold the name of the file
char* currentFileName = new char[currentHeader.FileNameLength + 1];
//check for a bad memory allocation
if (currentFileName == (char*)NULL)
throw __LZMA_BAD_MEMORY_ALLOC__;
//get the name of the file
fread((void*)currentFileName, sizeof(char), currentHeader.FileNameLength, RawData);
currentFileName[currentHeader.FileNameLength] = '\0';
//store the file name
string fileNameToStore = string(currentFileName);
this->StoredFiles.emplace_back(fileNameToStore);
//release the memory used to store a copy of the current file name
delete currentFileName;
//jump to the next header
fseek(RawData, currentHeader.CompressedDataLength, SEEK_CUR);
}
else
{
throw __LZMA_INVALID_HEADER__;
}
} while (!feof(RawData));
}
else
{
this->Exists = false;
}
}
//close the file
fclose(RawData);
}
VirtualFileSystem::~VirtualFileSystem(void)
{
//release the memory used to store the name of files inside the current lzma file
this->StoredFiles.clear();
this->StoredFiles.resize(0);
}
void VirtualFileSystem::WriteFile(string realPath, string fakeName)
{
//check if a file with the same name already exists
bool found = false;
for each (string var in this->StoredFiles)
{
if (var.compare(fakeName) == 0)
found = true;
}
//throw an exception if so
if (found == true)
throw __LZMA_FILE_ALREADY_EXISTS__;
//try to open the file
FILE* RawData;
//check if the file exists
if (this->Exists)
RawData = fopen(this->LZMAFilePath.c_str(), "a+");
else
RawData = fopen(this->LZMAFilePath.c_str(), "wb");
//check errors
if (RawData == (FILE*)NULL)
throw __LZMA_CANNOT_WRITE_FILE__;
//try to open the file to compress
FILE* FileToCompress = fopen(realPath.c_str(), "rb");
//check if the operation successed
if (FileToCompress == (FILE*)NULL)
throw __LZMA_CANNOT_OPEN_ORIGIN__;
//get the length of the file to compress
fseek(FileToCompress, 0, SEEK_END);
size_t OriginalLength = ftell(FileToCompress);
rewind(FileToCompress);
//check the length of the file
if (OriginalLength > 0)
{
//read the data that will be compressed
char* buffer = new char[OriginalLength + 1];
fread(buffer, sizeof(char), OriginalLength, FileToCompress);
buffer[OriginalLength] = '\0';
//prepare structures that will be used to hold compressed and uncompressed data
vector<unsigned char> DataToCompress(buffer, buffer + OriginalLength);
vector<unsigned char> CompressedData;
//clean the buffer used to read the file to compress
delete buffer;
//compress data
RawCompress(CompressedData, DataToCompress);
//get the MD5 of both rappresentation of the same data: compressed and uncompressed:
//prepare two digest holders
Digest MD5OfCompressedData, MD5OfUncompressedData;
//compute the digest of the compressed data
Digest* temp = MD5Vector(CompressedData);
memcpy(&MD5OfCompressedData, temp, sizeof(Digest));
//release the memory used to hold a copy of the digest of the compressed data
delete temp;
//compute the digest of the uncompressed data
temp = MD5Vector(DataToCompress);
memcpy(&MD5OfUncompressedData, temp, sizeof(Digest));
//release the memory used to hold a copy of the digest of the uncompressed data
delete temp;
//calculate the CRC of compressed data
CRC32 CRCOfCompressedData = CRCVector(CompressedData);
//build the header of the current compressed file
LZMAFileHeader header;
header.CompressedDataLength = CompressedData.size();
header.RawDataLength = DataToCompress.size();
header.FileNameLength = fakeName.length();
memcpy(&header.CRC, &CRCOfCompressedData, sizeof(CRC32));
memcpy(&header.MD5CompressedData, &MD5OfCompressedData, sizeof(Digest));
memcpy(&header.MD5UncompressedData, &MD5OfUncompressedData, sizeof(Digest));
//release the memory used to store the uncompressed data
DataToCompress.clear();
DataToCompress.resize(0);
//prepare the name of the file
char* nameOfCompressedFile = new char[fakeName.length() + 1];
memcpy(nameOfCompressedFile, fakeName.c_str(), fakeName.length());
nameOfCompressedFile[fakeName.length()] = '\0';
//write everything to file
fwrite(&header, sizeof(LZMAFileHeader), 1, RawData);
fwrite(nameOfCompressedFile, sizeof(char), strlen(nameOfCompressedFile), RawData);
for (size_t j = 0; j < CompressedData.size(); j++)
{
char ch = (char)CompressedData[j];
fwrite(&ch, sizeof(char), 1, RawData);
}
//track the new added file
this->StoredFiles.emplace_back(fakeName);
//the file exists now
this->Exists = true;
}
else
{
throw __LZMA_NOTHING_TO_COMPRESS__;
}
//close files
fclose(RawData);
fclose(FileToCompress);
}