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