1 // Description:
2 // Simple pak file type resouce manager.
3 //
4 // Copyright (C) 2001 Frank Becker
5 //
6 // This program is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free Software
8 // Foundation; either version 2 of the License, or (at your option) any later
9 // version.
10 //
11 // This program is distributed in the hope that it will be useful, but WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
14 //
15 #include <unistd.h>
16 #include <cstring>
17 #include <iomanip>
18 #include <dirent.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21
22 #include <Trace.hpp>
23 #include <ResourceManager.hpp>
24 #include <FindHash.hpp>
25 #include <Endian.hpp>
26
27 #ifdef WIN32
28 const char PATH_SEPERATOR = '\\';
29 #else
30 const char PATH_SEPERATOR = '/';
31 #endif
32
33 //Destroy resource manager
~ResourceManager()34 ResourceManager::~ResourceManager()
35 {
36 hash_map< const string, DirectoryEntry*, hash<const string> >::const_iterator ci;
37 for( ci=_dirEntryMap.begin(); ci!=_dirEntryMap.end(); ci++)
38 {
39 delete ci->second;
40 }
41 _dirEntryMap.clear();
42
43 delete _activeInput;
44 _activeInput = 0;
45
46 if( _infile.is_open()) _infile.close();
47 }
48
addResourceDirectory(const string & dirName)49 void ResourceManager::addResourceDirectory( const string &dirName)
50 {
51 DIR *dir = opendir( dirName.c_str());
52 if( !dir)
53 {
54 LOG_WARNING << "Problem opening directory: " << dirName << endl;
55 LOG_WARNING << "Skipping..." << endl;
56 return;
57 }
58
59 dirent *dirEntry;
60 while( (dirEntry = readdir( dir)) != 0)
61 {
62 string fileName = dirEntry->d_name;
63 if( (fileName == ".") || (fileName == "..")) continue;
64
65 string fullName = dirName + PATH_SEPERATOR + fileName;
66 if( dirName == ".")
67 {
68 fullName = fileName;
69 }
70
71 struct stat statInfo;
72 if( stat( fullName.c_str(), &statInfo) == -1)
73 {
74 LOG_WARNING << "Problem accessing file: " << fullName << endl;
75 LOG_WARNING << "Skipping..." << endl;
76 continue;
77 }
78
79 if( S_ISDIR( statInfo.st_mode))
80 {
81 addResourceDirectory( fullName);
82 }
83 else if( S_ISREG( statInfo.st_mode))
84 {
85 DirectoryEntry *de = new DirectoryEntry;
86 de->resourcePackFilename = fullName;
87 de->resourceName = fullName;
88 de->offset = 0;
89 de->origSize = statInfo.st_size;
90 de->compSize = statInfo.st_size;
91
92 _dirEntryMap[ de->resourceName] = de;
93 }
94 }
95 }
96
97 //
addResourcePack(const string & ifilename,const string & basedir)98 bool ResourceManager::addResourcePack(
99 const string &ifilename, const string &basedir)
100 {
101 string filename = ifilename;
102 ifstream infile( filename.c_str(), ios::in | ios::binary);
103 if( !infile.is_open())
104 {
105 filename = basedir+ifilename;
106
107 infile.clear();
108 infile.open( filename.c_str(), ios::in | ios::binary);
109 if( !infile.is_open())
110 {
111 LOG_ERROR << "Unable to open resource file: " << filename << endl;
112 return false;
113 }
114 LOG_INFO << "Using global resource file." << endl;
115 }
116
117 streamoff offset = -HEADER_SIZE;
118 infile.seekg( offset, ios::end);
119
120 Uint32 magic;
121 Uint32 dirLen;
122 infile >> dirLen;
123 infile >> magic;
124
125 if( magic != RESOURCE_MAGIC)
126 {
127 LOG_ERROR << "Incorrect packfile signature! ["
128 << filename << "]" << endl;
129 return false;
130 }
131
132 offset = -(dirLen+HEADER_SIZE);
133 infile.seekg( offset, ios::end);
134
135 Uint32 dataLen;
136 infile >> dataLen;
137 dirLen -= 4;
138
139 char *buff = new char[ dirLen];
140 infile.read( buff, dirLen);
141
142 LOG_INFO << "Dirlen = " << dirLen << ", Read = " << infile.gcount() << endl;
143 LOG_INFO << "Loading: " << filename << endl;
144
145 unsigned int pos = 0;
146 while( pos < dirLen)
147 {
148 DirectoryEntry *pde = new DirectoryEntry;
149 DirectoryEntry &de = *pde;
150 de.resourcePackFilename = filename;
151 de.resourceName = &buff[pos];
152 pos += de.resourceName.length()+1;
153
154 memcpy( &de.offset, &buff[pos], 4);
155 pos += 4;
156
157 memcpy( &de.origSize, &buff[pos], 4);
158 pos += 4;
159
160 memcpy( &de.compSize, &buff[pos], 4);
161 pos += 4;
162
163 if( ! ::isLittleEndian())
164 {
165 swap( &de.offset, 4);
166 swap( &de.origSize, 4);
167 swap( &de.compSize, 4);
168 }
169 LOG_INFO << " "
170 << de.resourceName << " ("
171 << de.origSize << "/"
172 << de.compSize << ") "
173 // << " Pos = " << pos
174 << endl;
175
176 _dirEntryMap[ de.resourceName] = pde;
177 }
178
179 delete[] buff;
180
181 return true;
182 }
183
updateInfile(const string & inputFile)184 void ResourceManager::updateInfile( const string &inputFile)
185 {
186 if( inputFile != _openFilename)
187 {
188 if( _infile.is_open())
189 {
190 _infile.close();
191 }
192
193 _infile.clear();
194 _infile.open( inputFile.c_str(), ios::in | ios::binary);
195 _openFilename = inputFile;
196 }
197 }
198
getResourceSize(const string & name)199 int ResourceManager::getResourceSize( const string &name)
200 {
201 DirectoryEntry *de = findHash( name, _dirEntryMap);
202 if( !de)
203 {
204 ifstream tmpfile;
205 tmpfile.open( name.c_str(), ios::in | ios::binary);
206 if( ! tmpfile.is_open())
207 {
208 return -1;
209 }
210 tmpfile.seekg (0, ios::end);
211 int length = tmpfile.tellg();
212 return length;
213 }
214 else
215 {
216 return de->origSize;
217 }
218
219 //shouldn't get here
220 return -1;
221 }
222
selectResource(const string & name)223 bool ResourceManager::selectResource( const string &name)
224 {
225 if( _activeInput)
226 {
227 delete _activeInput;
228 _activeInput = 0;
229 }
230
231 //check if resource exists as a plain file
232 struct stat statInfo;
233 if( ( stat( name.c_str(), &statInfo) == 0) &&
234 ( S_ISREG( statInfo.st_mode)))
235 {
236 updateInfile( name);
237 if( !_infile.is_open()) return false;
238 _infile.seekg( 0);
239 }
240 else
241 {
242 //resource not found in filesystem, try resourcepack
243 DirectoryEntry *de = findHash( name, _dirEntryMap);
244 if( de)
245 {
246 updateInfile( de->resourcePackFilename);
247 _infile.clear();
248 _infile.seekg( de->offset, ios::beg);
249 }
250 else
251 {
252 //unable to find resource anywhere
253 return false;
254 }
255 }
256
257 if( ! _infile.good())
258 {
259 LOG_ERROR << "INFILE BAD... (gcc3.0 again?)" << endl;
260 }
261
262 _activeInput = new ziStream( _infile);
263 if( !_activeInput)
264 {
265 return false;
266 }
267
268 if( !_activeInput->isOK())
269 {
270 delete _activeInput;
271 _activeInput = 0;
272 return false;
273 }
274
275 return true;
276 }
277
getInputStream(void)278 ziStream &ResourceManager::getInputStream( void)
279 {
280 return( *_activeInput);
281 }
282
getResourceList(list<string> & rNameList)283 void ResourceManager::getResourceList( list<string> &rNameList)
284 {
285 hash_map< const string, DirectoryEntry*, hash<const string> >::const_iterator ci;
286 for( ci=_dirEntryMap.begin(); ci!=_dirEntryMap.end(); ci++)
287 {
288 rNameList.insert( rNameList.end(), ci->second->resourceName);
289 }
290 }
291
dump(void)292 void ResourceManager::dump( void)
293 {
294 int totalOrig = 0;
295 int totalComp = 0;
296
297 hash_map< const string, DirectoryEntry*, hash<const string> >::const_iterator ci;
298 for( ci=_dirEntryMap.begin(); ci!=_dirEntryMap.end(); ci++)
299 {
300 DirectoryEntry &di = *(ci->second);
301 string name = di.resourcePackFilename +":"+ di.resourceName;
302 LOG_INFO.width(50);
303 LOG_VOID.fill('.');
304 LOG_VOID.setf( ios::left);
305 LOG_VOID << name.c_str();
306
307 LOG_VOID.width(12);
308 LOG_VOID.setf( ios::right);
309 LOG_VOID << di.origSize;
310
311 LOG_VOID << " (" << di.compSize << ")" << endl;
312
313 totalOrig += di.origSize;
314 totalComp += di.compSize;
315 }
316 LOG_INFO << totalOrig << " (" << totalComp << ") bytes." << endl;
317 }
318