1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // Copyright (C) 2009-2012 Christian Stehno
3 // This file is part of the "Irrlicht Engine".
4 // For conditions of distribution and use, see copyright notice in irrlicht.h
5 // Based on the NPK reader from Irrlicht
6
7 #include "CNPKReader.h"
8
9 #ifdef __IRR_COMPILE_WITH_NPK_ARCHIVE_LOADER_
10
11 #include "os.h"
12 #include "coreutil.h"
13
14 #ifdef _DEBUG
15 #define IRR_DEBUG_NPK_READER
16 #endif
17
18 namespace irr
19 {
20 namespace io
21 {
22
23 namespace
24 {
isHeaderValid(const SNPKHeader & header)25 bool isHeaderValid(const SNPKHeader& header)
26 {
27 const c8* const tag = header.Tag;
28 return tag[0] == '0' &&
29 tag[1] == 'K' &&
30 tag[2] == 'P' &&
31 tag[3] == 'N';
32 }
33 } // end namespace
34
35
36 //! Constructor
CArchiveLoaderNPK(io::IFileSystem * fs)37 CArchiveLoaderNPK::CArchiveLoaderNPK( io::IFileSystem* fs)
38 : FileSystem(fs)
39 {
40 #ifdef _DEBUG
41 setDebugName("CArchiveLoaderNPK");
42 #endif
43 }
44
45
46 //! returns true if the file maybe is able to be loaded by this class
isALoadableFileFormat(const io::path & filename) const47 bool CArchiveLoaderNPK::isALoadableFileFormat(const io::path& filename) const
48 {
49 return core::hasFileExtension(filename, "npk");
50 }
51
52 //! Check to see if the loader can create archives of this type.
isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const53 bool CArchiveLoaderNPK::isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const
54 {
55 return fileType == EFAT_NPK;
56 }
57
58 //! Creates an archive from the filename
59 /** \param file File handle to check.
60 \return Pointer to newly created archive, or 0 upon error. */
createArchive(const io::path & filename,bool ignoreCase,bool ignorePaths) const61 IFileArchive* CArchiveLoaderNPK::createArchive(const io::path& filename, bool ignoreCase, bool ignorePaths) const
62 {
63 IFileArchive *archive = 0;
64 io::IReadFile* file = FileSystem->createAndOpenFile(filename);
65
66 if (file)
67 {
68 archive = createArchive(file, ignoreCase, ignorePaths);
69 file->drop ();
70 }
71
72 return archive;
73 }
74
75 //! creates/loads an archive from the file.
76 //! \return Pointer to the created archive. Returns 0 if loading failed.
createArchive(io::IReadFile * file,bool ignoreCase,bool ignorePaths) const77 IFileArchive* CArchiveLoaderNPK::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
78 {
79 IFileArchive *archive = 0;
80 if ( file )
81 {
82 file->seek ( 0 );
83 archive = new CNPKReader(file, ignoreCase, ignorePaths);
84 }
85 return archive;
86 }
87
88
89 //! Check if the file might be loaded by this class
90 /** Check might look into the file.
91 \param file File handle to check.
92 \return True if file seems to be loadable. */
isALoadableFileFormat(io::IReadFile * file) const93 bool CArchiveLoaderNPK::isALoadableFileFormat(io::IReadFile* file) const
94 {
95 SNPKHeader header;
96
97 file->read(&header, sizeof(header));
98
99 return isHeaderValid(header);
100 }
101
102
103 /*!
104 NPK Reader
105 */
CNPKReader(IReadFile * file,bool ignoreCase,bool ignorePaths)106 CNPKReader::CNPKReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
107 : CFileList((file ? file->getFileName() : io::path("")), ignoreCase, ignorePaths), File(file)
108 {
109 #ifdef _DEBUG
110 setDebugName("CNPKReader");
111 #endif
112
113 if (File)
114 {
115 File->grab();
116 if (scanLocalHeader())
117 sort();
118 else
119 os::Printer::log("Failed to load NPK archive.");
120 }
121 }
122
123
~CNPKReader()124 CNPKReader::~CNPKReader()
125 {
126 if (File)
127 File->drop();
128 }
129
130
getFileList() const131 const IFileList* CNPKReader::getFileList() const
132 {
133 return this;
134 }
135
136
scanLocalHeader()137 bool CNPKReader::scanLocalHeader()
138 {
139 SNPKHeader header;
140
141 // Read and validate the header
142 File->read(&header, sizeof(header));
143 if (!isHeaderValid(header))
144 return false;
145
146 // Seek to the table of contents
147 #ifdef __BIG_ENDIAN__
148 header.Offset = os::Byteswap::byteswap(header.Offset);
149 header.Length = os::Byteswap::byteswap(header.Length);
150 #endif
151 header.Offset += 8;
152 core::stringc dirName;
153 bool inTOC=true;
154 // Loop through each entry in the table of contents
155 while (inTOC && (File->getPos() < File->getSize()))
156 {
157 // read an entry
158 char tag[4]={0};
159 SNPKFileEntry entry;
160 File->read(tag, 4);
161 const int numTag = MAKE_IRR_ID(tag[3],tag[2],tag[1],tag[0]);
162 int size;
163
164 bool isDir=true;
165
166 switch (numTag)
167 {
168 case MAKE_IRR_ID('D','I','R','_'):
169 {
170 File->read(&size, 4);
171 readString(entry.Name);
172 entry.Length=0;
173 entry.Offset=0;
174 #ifdef IRR_DEBUG_NPK_READER
175 os::Printer::log("Dir", entry.Name);
176 #endif
177 }
178 break;
179 case MAKE_IRR_ID('F','I','L','E'):
180 {
181 File->read(&size, 4);
182 File->read(&entry.Offset, 4);
183 File->read(&entry.Length, 4);
184 readString(entry.Name);
185 isDir=false;
186 #ifdef IRR_DEBUG_NPK_READER
187 os::Printer::log("File", entry.Name);
188 #endif
189 #ifdef __BIG_ENDIAN__
190 entry.Offset = os::Byteswap::byteswap(entry.Offset);
191 entry.Length = os::Byteswap::byteswap(entry.Length);
192 #endif
193 }
194 break;
195 case MAKE_IRR_ID('D','E','N','D'):
196 {
197 File->read(&size, 4);
198 entry.Name="";
199 entry.Length=0;
200 entry.Offset=0;
201 const s32 pos = dirName.findLast('/', dirName.size()-2);
202 if (pos==-1)
203 dirName="";
204 else
205 dirName=dirName.subString(0, pos);
206 #ifdef IRR_DEBUG_NPK_READER
207 os::Printer::log("Dirend", dirName);
208 #endif
209 }
210 break;
211 default:
212 inTOC=false;
213 }
214 // skip root dir
215 if (isDir)
216 {
217 if (!entry.Name.size() || (entry.Name==".") || (entry.Name=="<noname>"))
218 continue;
219 dirName += entry.Name;
220 dirName += "/";
221 }
222 #ifdef IRR_DEBUG_NPK_READER
223 os::Printer::log("Name", entry.Name);
224 #endif
225 addItem((isDir?dirName:dirName+entry.Name), entry.Offset+header.Offset, entry.Length, isDir);
226 }
227 return true;
228 }
229
230
231 //! opens a file by file name
createAndOpenFile(const io::path & filename)232 IReadFile* CNPKReader::createAndOpenFile(const io::path& filename)
233 {
234 s32 index = findFile(filename, false);
235
236 if (index != -1)
237 return createAndOpenFile(index);
238
239 return 0;
240 }
241
242
243 //! opens a file by index
createAndOpenFile(u32 index)244 IReadFile* CNPKReader::createAndOpenFile(u32 index)
245 {
246 if (index >= Files.size() )
247 return 0;
248
249 const SFileListEntry &entry = Files[index];
250 return createLimitReadFile( entry.FullName, File, entry.Offset, entry.Size );
251 }
252
readString(core::stringc & name)253 void CNPKReader::readString(core::stringc& name)
254 {
255 short stringSize;
256 char buf[256];
257 File->read(&stringSize, 2);
258 #ifdef __BIG_ENDIAN__
259 stringSize = os::Byteswap::byteswap(stringSize);
260 #endif
261 name.reserve(stringSize);
262 while(stringSize)
263 {
264 const short next = core::min_(stringSize, (short)255);
265 File->read(buf,next);
266 buf[next]=0;
267 name.append(buf);
268 stringSize -= next;
269 }
270 }
271
272
273 } // end namespace io
274 } // end namespace irr
275
276 #endif // __IRR_COMPILE_WITH_NPK_ARCHIVE_LOADER_
277
278