irrlicht/source/Irrlicht/CTarReader.cpp

257 lines
5.6 KiB
C++

// Copyright (C) 2009-2012 Gaz Davidson
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "CTarReader.h"
#ifdef __IRR_COMPILE_WITH_TAR_ARCHIVE_LOADER_
#include "CFileList.h"
#include "CLimitReadFile.h"
#include "os.h"
#include "coreutil.h"
#include "errno.h"
namespace irr
{
namespace io
{
//! Constructor
CArchiveLoaderTAR::CArchiveLoaderTAR(io::IFileSystem* fs)
: FileSystem(fs)
{
#ifdef _DEBUG
setDebugName("CArchiveLoaderTAR");
#endif
}
//! returns true if the file maybe is able to be loaded by this class
bool CArchiveLoaderTAR::isALoadableFileFormat(const io::path& filename) const
{
return core::hasFileExtension(filename, "tar");
}
//! Check to see if the loader can create archives of this type.
bool CArchiveLoaderTAR::isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const
{
return fileType == EFAT_TAR;
}
//! Creates an archive from the filename
/** \param file File handle to check.
\return Pointer to newly created archive, or 0 upon error. */
IFileArchive* CArchiveLoaderTAR::createArchive(const io::path& filename, bool ignoreCase, bool ignorePaths) const
{
IFileArchive *archive = 0;
io::IReadFile* file = FileSystem->createAndOpenFile(filename);
if (file)
{
archive = createArchive(file, ignoreCase, ignorePaths);
file->drop();
}
return archive;
}
//! creates/loads an archive from the file.
//! \return Pointer to the created archive. Returns 0 if loading failed.
IFileArchive* CArchiveLoaderTAR::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
{
IFileArchive *archive = 0;
if (file)
{
file->seek(0);
archive = new CTarReader(file, ignoreCase, ignorePaths);
}
return archive;
}
//! Check if the file might be loaded by this class
/** Check might look into the file.
\param file File handle to check.
\return True if file seems to be loadable. */
bool CArchiveLoaderTAR::isALoadableFileFormat(io::IReadFile* file) const
{
// TAR files consist of blocks of 512 bytes
// if it isn't a multiple of 512 then it's not a TAR file.
if (file->getSize() % 512)
return false;
file->seek(0);
// read header of first file
STarHeader fHead;
file->read(&fHead, sizeof(STarHeader));
u32 checksum = 0;
sscanf(fHead.Checksum, "%o", &checksum);
// verify checksum
// some old TAR writers assume that chars are signed, others assume unsigned
// USTAR archives have a longer header, old TAR archives end after linkname
u32 checksum1=0;
s32 checksum2=0;
// remember to blank the checksum field!
memset(fHead.Checksum, ' ', 8);
// old header
for (u8* p = (u8*)(&fHead); p < (u8*)(&fHead.Magic[0]); ++p)
{
checksum1 += *p;
checksum2 += c8(*p);
}
if (!strncmp(fHead.Magic, "ustar", 5))
{
for (u8* p = (u8*)(&fHead.Magic[0]); p < (u8*)(&fHead) + sizeof(fHead); ++p)
{
checksum1 += *p;
checksum2 += c8(*p);
}
}
return checksum1 == checksum || checksum2 == (s32)checksum;
}
/*
TAR Archive
*/
CTarReader::CTarReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
: CFileList((file ? file->getFileName() : io::path("")), ignoreCase, ignorePaths), File(file)
{
#ifdef _DEBUG
setDebugName("CTarReader");
#endif
if (File)
{
File->grab();
// fill the file list
populateFileList();
sort();
}
}
CTarReader::~CTarReader()
{
if (File)
File->drop();
}
const IFileList* CTarReader::getFileList() const
{
return this;
}
u32 CTarReader::populateFileList()
{
STarHeader fHead;
Files.clear();
u32 pos = 0;
while ( s32(pos + sizeof(STarHeader)) < File->getSize())
{
// seek to next file header
File->seek(pos);
// read the header
File->read(&fHead, sizeof(fHead));
// only add standard files for now
if (fHead.Link == ETLI_REGULAR_FILE || ETLI_REGULAR_FILE_OLD)
{
io::path fullPath = "";
fullPath.reserve(255);
// USTAR archives have a filename prefix
// may not be null terminated, copy carefully!
if (!strncmp(fHead.Magic, "ustar", 5))
{
c8* np = fHead.FileNamePrefix;
while(*np && (np - fHead.FileNamePrefix) < 155)
fullPath.append(*np);
np++;
}
// append the file name
c8* np = fHead.FileName;
while(*np && (np - fHead.FileName) < 100)
{
fullPath.append(*np);
np++;
}
// get size
core::stringc sSize = "";
sSize.reserve(12);
np = fHead.Size;
while(*np && (np - fHead.Size) < 12)
{
sSize.append(*np);
np++;
}
u32 size = strtoul(sSize.c_str(), NULL, 8);
if (errno == ERANGE)
os::Printer::log("File too large", fullPath, ELL_WARNING);
// save start position
u32 offset = pos + 512;
// move to next file header block
pos = offset + (size / 512) * 512 + ((size % 512) ? 512 : 0);
// add file to list
addItem(fullPath, offset, size, false );
}
else
{
// todo: ETLI_DIRECTORY, ETLI_LINK_TO_ARCHIVED_FILE
// move to next block
pos += 512;
}
}
return Files.size();
}
//! opens a file by file name
IReadFile* CTarReader::createAndOpenFile(const io::path& filename)
{
const s32 index = findFile(filename, false);
if (index != -1)
return createAndOpenFile(index);
return 0;
}
//! opens a file by index
IReadFile* CTarReader::createAndOpenFile(u32 index)
{
if (index >= Files.size() )
return 0;
const SFileListEntry &entry = Files[index];
return createLimitReadFile( entry.FullName, File, entry.Offset, entry.Size );
}
} // end namespace io
} // end namespace irr
#endif // __IRR_COMPILE_WITH_TAR_ARCHIVE_LOADER_