227 lines
4.1 KiB
C++
227 lines
4.1 KiB
C++
|
// Copyright (C) 2002-2007 Nikolaus Gebhardt
|
||
|
// This file is part of the "Irrlicht Engine".
|
||
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||
|
// Code contributed by skreamz
|
||
|
|
||
|
#include "CPakReader.h"
|
||
|
#include "os.h"
|
||
|
|
||
|
#include "IrrCompileConfig.h"
|
||
|
|
||
|
namespace irr
|
||
|
{
|
||
|
namespace io
|
||
|
{
|
||
|
|
||
|
|
||
|
CPakReader::CPakReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
|
||
|
: File(file), IgnoreCase(ignoreCase), IgnorePaths(ignorePaths)
|
||
|
{
|
||
|
#ifdef _DEBUG
|
||
|
setDebugName("CPakReader");
|
||
|
#endif
|
||
|
|
||
|
if (File)
|
||
|
{
|
||
|
File->grab();
|
||
|
|
||
|
// scan local headers
|
||
|
scanLocalHeader();
|
||
|
|
||
|
// prepare file index for binary search
|
||
|
FileList.sort();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CPakReader::~CPakReader()
|
||
|
{
|
||
|
if (File)
|
||
|
File->drop();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//! splits filename from zip file into useful filenames and paths
|
||
|
void CPakReader::extractFilename(SPakFileEntry* entry)
|
||
|
{
|
||
|
s32 lorfn = 56; // length of real file name
|
||
|
|
||
|
if (!lorfn)
|
||
|
return;
|
||
|
|
||
|
if (IgnoreCase)
|
||
|
entry->pakFileName.make_lower();
|
||
|
|
||
|
const c8* p = entry->pakFileName.c_str() + lorfn;
|
||
|
|
||
|
// suche ein slash oder den anfang.
|
||
|
|
||
|
while (*p!='/' && p!=entry->pakFileName.c_str())
|
||
|
{
|
||
|
--p;
|
||
|
--lorfn;
|
||
|
}
|
||
|
|
||
|
bool thereIsAPath = p != entry->pakFileName.c_str();
|
||
|
|
||
|
if (thereIsAPath)
|
||
|
{
|
||
|
// there is a path
|
||
|
++p;
|
||
|
++lorfn;
|
||
|
}
|
||
|
|
||
|
entry->simpleFileName = p;
|
||
|
entry->path = "";
|
||
|
|
||
|
// pfad auch kopieren
|
||
|
if (thereIsAPath)
|
||
|
{
|
||
|
lorfn = (s32)(p - entry->pakFileName.c_str());
|
||
|
entry->path.append(entry->pakFileName, lorfn);
|
||
|
}
|
||
|
|
||
|
if (!IgnorePaths)
|
||
|
entry->simpleFileName = entry->pakFileName; // thanks to Pr3t3nd3r for this fix
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//! scans for a local header, returns false if there is no more local file header.
|
||
|
bool CPakReader::scanLocalHeader()
|
||
|
{
|
||
|
c8 tmp[1024];
|
||
|
|
||
|
SPakFileEntry entry;
|
||
|
entry.pos = 0;
|
||
|
|
||
|
memset(&header, 0, sizeof(SPAKFileHeader));
|
||
|
File->read(&header, sizeof(SPAKFileHeader));
|
||
|
|
||
|
|
||
|
if (header.tag[0] != 'P' && header.tag[1] != 'A')
|
||
|
return false; // local file headers end here.
|
||
|
|
||
|
File->seek(header.offset);
|
||
|
|
||
|
int count = header.length / ((sizeof(u32) * 2) + 56);
|
||
|
|
||
|
for(int i = 0; i < count; i++)
|
||
|
{
|
||
|
// read filename
|
||
|
entry.pakFileName.reserve(56+2);
|
||
|
File->read(tmp, 56);
|
||
|
tmp[56] = 0x0;
|
||
|
entry.pakFileName = tmp;
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
os::Printer::log(entry.pakFileName.c_str());
|
||
|
#endif
|
||
|
|
||
|
extractFilename(&entry);
|
||
|
|
||
|
File->read(&entry.pos, sizeof(u32));
|
||
|
File->read(&entry.length, sizeof(u32));
|
||
|
FileList.push_back(entry);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//! opens a file by file name
|
||
|
IReadFile* CPakReader::openFile(const c8* filename)
|
||
|
{
|
||
|
s32 index = findFile(filename);
|
||
|
|
||
|
if (index != -1)
|
||
|
return openFile(index);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//! opens a file by index
|
||
|
IReadFile* CPakReader::openFile(s32 index)
|
||
|
{
|
||
|
File->seek(FileList[index].pos);
|
||
|
return createLimitReadFile(FileList[index].simpleFileName.c_str(), File, FileList[index].length);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//! returns count of files in archive
|
||
|
s32 CPakReader::getFileCount()
|
||
|
{
|
||
|
return FileList.size();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//! returns data of file
|
||
|
const SPakFileEntry* CPakReader::getFileInfo(s32 index) const
|
||
|
{
|
||
|
return &FileList[index];
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//! deletes the path from a filename
|
||
|
void CPakReader::deletePathFromFilename(core::stringc& filename)
|
||
|
{
|
||
|
// delete path from filename
|
||
|
const c8* p = filename.c_str() + filename.size();
|
||
|
|
||
|
// suche ein slash oder den anfang.
|
||
|
|
||
|
while (*p!='/' && *p!='\\' && p!=filename.c_str())
|
||
|
--p;
|
||
|
|
||
|
core::stringc newName;
|
||
|
|
||
|
if (p != filename.c_str())
|
||
|
{
|
||
|
++p;
|
||
|
filename = p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//! returns fileindex
|
||
|
s32 CPakReader::findFile(const c8* simpleFilename)
|
||
|
{
|
||
|
SPakFileEntry entry;
|
||
|
entry.simpleFileName = simpleFilename;
|
||
|
|
||
|
if (IgnoreCase)
|
||
|
entry.simpleFileName.make_lower();
|
||
|
|
||
|
if (IgnorePaths)
|
||
|
deletePathFromFilename(entry.simpleFileName);
|
||
|
|
||
|
s32 res = FileList.binary_search(entry);
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
if (res == -1)
|
||
|
{
|
||
|
for (u32 i=0; i<FileList.size(); ++i)
|
||
|
if (FileList[i].simpleFileName == entry.simpleFileName)
|
||
|
{
|
||
|
os::Printer::log("File in archive but not found.", entry.simpleFileName.c_str(), ELL_ERROR);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
} // end namespace io
|
||
|
} // end namespace irr
|
||
|
|