1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 // Code contributed by skreamz
5 
6 #include "CPakReader.h"
7 
8 #ifdef __IRR_COMPILE_WITH_PAK_ARCHIVE_LOADER_
9 
10 #include "os.h"
11 #include "coreutil.h"
12 
13 namespace irr
14 {
15 namespace io
16 {
17 
18 namespace
19 {
20 
isHeaderValid(const SPAKFileHeader & header)21 inline bool isHeaderValid(const SPAKFileHeader& header)
22 {
23 	const c8* tag = header.tag;
24 	return tag[0] == 'P' &&
25 		   tag[1] == 'A' &&
26 		   tag[2] == 'C' &&
27 		   tag[3] == 'K';
28 }
29 
30 } // end namespace
31 
32 //! Constructor
CArchiveLoaderPAK(io::IFileSystem * fs)33 CArchiveLoaderPAK::CArchiveLoaderPAK( io::IFileSystem* fs)
34 : FileSystem(fs)
35 {
36 #ifdef _DEBUG
37 	setDebugName("CArchiveLoaderPAK");
38 #endif
39 }
40 
41 
42 //! returns true if the file maybe is able to be loaded by this class
isALoadableFileFormat(const io::path & filename) const43 bool CArchiveLoaderPAK::isALoadableFileFormat(const io::path& filename) const
44 {
45 	return core::hasFileExtension(filename, "pak");
46 }
47 
48 //! Check to see if the loader can create archives of this type.
isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const49 bool CArchiveLoaderPAK::isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const
50 {
51 	return fileType == EFAT_PAK;
52 }
53 
54 //! Creates an archive from the filename
55 /** \param file File handle to check.
56 \return Pointer to newly created archive, or 0 upon error. */
createArchive(const io::path & filename,bool ignoreCase,bool ignorePaths) const57 IFileArchive* CArchiveLoaderPAK::createArchive(const io::path& filename, bool ignoreCase, bool ignorePaths) const
58 {
59 	IFileArchive *archive = 0;
60 	io::IReadFile* file = FileSystem->createAndOpenFile(filename);
61 
62 	if (file)
63 	{
64 		archive = createArchive(file, ignoreCase, ignorePaths);
65 		file->drop ();
66 	}
67 
68 	return archive;
69 }
70 
71 //! creates/loads an archive from the file.
72 //! \return Pointer to the created archive. Returns 0 if loading failed.
createArchive(io::IReadFile * file,bool ignoreCase,bool ignorePaths) const73 IFileArchive* CArchiveLoaderPAK::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
74 {
75 	IFileArchive *archive = 0;
76 	if ( file )
77 	{
78 		file->seek ( 0 );
79 		archive = new CPakReader(file, ignoreCase, ignorePaths);
80 	}
81 	return archive;
82 }
83 
84 
85 //! Check if the file might be loaded by this class
86 /** Check might look into the file.
87 \param file File handle to check.
88 \return True if file seems to be loadable. */
isALoadableFileFormat(io::IReadFile * file) const89 bool CArchiveLoaderPAK::isALoadableFileFormat(io::IReadFile* file) const
90 {
91 	SPAKFileHeader header;
92 
93 	file->read(&header, sizeof(header));
94 
95 	return isHeaderValid(header);
96 }
97 
98 
99 /*!
100 	PAK Reader
101 */
CPakReader(IReadFile * file,bool ignoreCase,bool ignorePaths)102 CPakReader::CPakReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
103 : CFileList((file ? file->getFileName() : io::path("")), ignoreCase, ignorePaths), File(file)
104 {
105 #ifdef _DEBUG
106 	setDebugName("CPakReader");
107 #endif
108 
109 	if (File)
110 	{
111 		File->grab();
112 		scanLocalHeader();
113 		sort();
114 	}
115 }
116 
117 
~CPakReader()118 CPakReader::~CPakReader()
119 {
120 	if (File)
121 		File->drop();
122 }
123 
124 
getFileList() const125 const IFileList* CPakReader::getFileList() const
126 {
127 	return this;
128 }
129 
scanLocalHeader()130 bool CPakReader::scanLocalHeader()
131 {
132 	SPAKFileHeader header;
133 
134 	// Read and validate the header
135 	File->read(&header, sizeof(header));
136 	if (!isHeaderValid(header))
137 		return false;
138 
139 	// Seek to the table of contents
140 #ifdef __BIG_ENDIAN__
141 	header.offset = os::Byteswap::byteswap(header.offset);
142 	header.length = os::Byteswap::byteswap(header.length);
143 #endif
144 	File->seek(header.offset);
145 
146 	const int numberOfFiles = header.length / sizeof(SPAKFileEntry);
147 
148 	// Loop through each entry in the table of contents
149 	for(int i = 0; i < numberOfFiles; i++)
150 	{
151 		// read an entry
152 		SPAKFileEntry entry;
153 		File->read(&entry, sizeof(entry));
154 
155 #ifdef _DEBUG
156 		os::Printer::log(entry.name);
157 #endif
158 
159 #ifdef __BIG_ENDIAN__
160 		entry.offset = os::Byteswap::byteswap(entry.offset);
161 		entry.length = os::Byteswap::byteswap(entry.length);
162 #endif
163 
164 		addItem(io::path(entry.name), entry.offset, entry.length, false );
165 	}
166 	return true;
167 }
168 
169 
170 //! opens a file by file name
createAndOpenFile(const io::path & filename)171 IReadFile* CPakReader::createAndOpenFile(const io::path& filename)
172 {
173 	s32 index = findFile(filename, false);
174 
175 	if (index != -1)
176 		return createAndOpenFile(index);
177 
178 	return 0;
179 }
180 
181 
182 //! opens a file by index
createAndOpenFile(u32 index)183 IReadFile* CPakReader::createAndOpenFile(u32 index)
184 {
185 	if (index >= Files.size() )
186 		return 0;
187 
188 	const SFileListEntry &entry = Files[index];
189 	return createLimitReadFile( entry.FullName, File, entry.Offset, entry.Size );
190 }
191 
192 } // end namespace io
193 } // end namespace irr
194 
195 #endif // __IRR_COMPILE_WITH_PAK_ARCHIVE_LOADER_
196 
197