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