MezzanineEngine 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
resourcemanager.cpp
1 // © Copyright 2010 - 2011 BlackTopp Studios Inc.
2 /* This file is part of The Mezzanine Engine.
3 
4  The Mezzanine Engine is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  The Mezzanine Engine is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with The Mezzanine Engine. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /* The original authors have included a copy of the license specified above in the
18  'Docs' folder. See 'gpl.txt'
19 */
20 /* We welcome the use of the Mezzanine engine to anyone, including companies who wish to
21  Build professional software and charge for their product.
22 
23  However there are some practical restrictions, so if your project involves
24  any of the following you should contact us and we will try to work something
25  out:
26  - DRM or Copy Protection of any kind(except Copyrights)
27  - Software Patents You Do Not Wish to Freely License
28  - Any Kind of Linking to Non-GPL licensed Works
29  - Are Currently In Violation of Another Copyright Holder's GPL License
30  - If You want to change our code and not add a few hundred MB of stuff to
31  your distribution
32 
33  These and other limitations could cause serious legal problems if you ignore
34  them, so it is best to simply contact us or the Free Software Foundation, if
35  you have any questions.
36 
37  Joseph Toppi - toppij@gmail.com
38  John Blackwood - makoenergy02@gmail.com
39 */
40 #ifndef _resourcemanager_cpp
41 #define _resourcemanager_cpp
42 
43 #include <iostream>
44 #include <fstream>
45 #include <cstdlib>
46 
47 #include "resourcemanager.h"
48 #include "Graphics/meshmanager.h"
49 #include "stringtool.h"
50 #include "Resource/filestream.h"
51 #include "Resource/memorystream.h"
52 #include "Internal/ogredatastreambuf.h.cpp"
53 
54 #include "Resource/resourceenumerations.h"
55 
56 #include <Ogre.h>
57 #include <btBulletWorldImporter.h>
58 #include <btBulletDynamicsCommon.h>
59 
60 #include <dirent.h>
61 #ifdef WINDOWS
62  #include <Winuser.h>
63  #include <WinBase.h>
64  #include <Shlobj.h> // for getting system directories
65  #include <direct.h> // for _getcwd
66 #elif MACOSX
67  #include <CoreServices/CoreServices.h>
68  #include <unistd.h>//for sleep and getcwd
69  #include <errno.h>
70  #include <sys/stat.h>
71  #include <sys/types.h>
72  #include "pwd.h"
73  #include <mach-o/dyld.h> // for _NSGetExecutablePath
74 #else
75  #include <unistd.h>//for sleep and getcwd
76  #include <errno.h>
77  #include <sys/stat.h>
78  #include <sys/types.h>
79  #include <pwd.h>
80 #endif
81 
82 
83 #ifdef MEZZDEBUG
84 #include "entresol.h"
85 #endif
86 
87 #ifdef CreateDirectory
88 #undef CreateDirectory
89 #endif
90 
91 #ifdef RemoveDirectory
92 #undef RemoveDirectory
93 #endif
94 
95 namespace Mezzanine
96 {
97  template<> ResourceManager* Singleton<ResourceManager>::SingletonPtr = NULL;
98 
99  ResourceManager::ResourceManager(const String& EngineDataPath, const Mezzanine::ArchiveType ArchType, int ArgCount, char** ArgVars)
100  {
101  this->OgreResource = Ogre::ResourceGroupManager::getSingletonPtr();
102  this->EngineDataDir = EngineDataPath;
103  this->AddAssetLocation(EngineDataPath, ArchType, "EngineData", false);
104  this->SetMainArgs(ArgCount, ArgVars);
105  }
106 
108  {
109  OgreResource = Ogre::ResourceGroupManager::getSingletonPtr();
110  /// @todo This class currently doesn't initialize anything from XML, if that changes this constructor needs to be expanded.
111  }
112 
114  {
115  this->Deinitialize();
116 
117  for(std::vector<ResourceInputStream*>::iterator Iter = DeleteList.begin(); Iter != DeleteList.end(); Iter++)
118  { delete *Iter; }
119  }
120 
121  void ResourceManager::SetMainArgs(int ArgCount, char** ArgVars)
122  {
123  this->ArgC = ArgCount;
124  this->ArgV = ArgVars;
125  }
126 
127  ///////////////////////////////////////////////////////////////////////////////
128  // Directory/Path Management
129 
130  bool ResourceManager::CreateDirectory(const String& DirectoryPath)
131  {
132  #ifdef WINDOWS
133  if(::CreateDirectoryA(DirectoryPath.c_str(),NULL) < 0)
134  {
135  if(ERROR_ALREADY_EXISTS == ::GetLastError())
136  {
137  return false;
138  }
139  StringStream ExceptionStream;
140  ExceptionStream << "Unable to create directory. Error follows:" << std::endl;
141  if(ERROR_PATH_NOT_FOUND == ::GetLastError())
142  {
143  ExceptionStream << "Path to requested directory does not exist.";
144  }
145  else
146  {
147  ExceptionStream << "Error Unknown. :(";
148  }
149  MEZZ_EXCEPTION(Exception::IO_DIRECTORY_NOT_FOUND_EXCEPTION,ExceptionStream.str());
150  }
151  return true;
152  #else
153  if(::mkdir(DirectoryPath.c_str(),0777) < 0)
154  {
155  if( EEXIST == errno )
156  {
157  return false;
158  }
159  StringStream ExceptionStream;
160  ExceptionStream << "Unable to create directory. Error follows:" << std::endl;
161  ExceptionStream << strerror(errno);
162  MEZZ_EXCEPTION(Exception::IO_DIRECTORY_NOT_FOUND_EXCEPTION,ExceptionStream.str());
163  }
164  return true;
165  #endif
166  }
167 
168  bool ResourceManager::DoesDirectoryExist(const String& DirectoryPath)
169  {
170  struct stat st;
171  if((stat(DirectoryPath.c_str(),&st) == 0))
172  {
173  return S_ISDIR(st.st_mode);
174  }else{
175  //MEZZ_EXCEPTION(Exception::IO_DIRECTORY_NOT_FOUND_EXCEPTION,"Unknown error getting directory information.");
176  return false;
177  }
178  }
179 
180  void ResourceManager::RemoveDirectory(const String& DirectoryPath)
181  {
182  if(!rmdir(DirectoryPath.c_str()))
183  {
184  return;
185  }else{
186  MEZZ_EXCEPTION(Exception::IO_DIRECTORY_NOT_FOUND_EXCEPTION,"Unknown error removing directory.");
187  }
188  }
189 
191  {
192  Whole Last(FileName.find_last_of("\\/"));
193  if(FileName.npos == Last)
194  { return String(""); }
195  else
196  {
197  if(Last<FileName.size())
198  { return FileName.substr(0,Last+1); }
199  else
200  { return FileName.substr(0,Last+1); }
201  }
202  }
203 
205  {
206  Whole Last(FileName.find_last_of("\\/"));
207  if(FileName.npos == Last)
208  { return FileName; }
209  else
210  {
211  if(Last<FileName.size())
212  { return FileName.substr(Last+1,FileName.npos); }
213  else
214  { return String(""); }
215  }
216  }
217 
219  {
220  #ifdef WINDOWS
221  return '\\';
222  #else
223  return '/';
224  #endif
225  }
226 
228  {
229  #ifdef WINDOWS
230  return ';';
231  #else
232  return ':';
233  #endif
234  }
235 
237  {
238  StringVector Results;
239  const char Sep=GetPathSeparator();
240  String OneEntry;
241 
242  for(String::const_iterator Current = PATH.begin();
243  PATH.end()!=Current;
244  Current++)
245  {
246  if(Sep==*Current)
247  {
248  Results.push_back(OneEntry);
249  OneEntry.clear();
250  }else{
251  OneEntry+=*Current;
252  }
253  }
254  return Results;
255  }
256 
258  {
260 
261  for(StringVector::const_iterator Iter = PATH.begin();
262  Iter!=PATH.end();
263  Iter++)
264  {
265  StringSet Entries(GetDirContents(*Iter));
266  if(Entries.find(ExecutableName)!=Entries.end())
267  { return *Iter + GetDirectorySeparator(); }
268  }
269  return String();
270  }
271 
272  String ResourceManager::GetExecutableDirFromArg(int ArgCount, char** ArgVars)
273  {
274  if(ArgCount>0)
275  {
276  if(String("") == BaseName(ArgVars[0])) // No command is clearly bogus, must bail
277  { return ""; }
278 
279 
280  String Results(DirName(ArgVars[0]));
281 
282  // strip ./ ../ .\ ..\ and
283  //String::iterator From=Results.begin();
284  //for(String::iterator Iter=Results.begin(); Iter!=Results.end();)
285  //{}
286 
287  if( String("")==Results || String("./")==Results || String(".\\")==Results)// common cases of exe existing but dir is empty
288  { return "."; }
289 
290  if(String("")!=Results) // and exe is empty.
291  { return Results; }
292  return String("");
293  }else{
294  return String("");
295  }
296  }
297 
299  { return GetExecutableDirFromArg(ArgC,ArgV); }
300 
302  {
303  char Results[FILENAME_MAX];
304  #ifdef LINUX
305  MaxInt Length = ::readlink("/proc/self/exe", Results, sizeof(Results)-1);
306  if (Length != -1)
307  {
308  Results[Length] = '\0';
309  return DirName(String(Results));
310  } else {
311  return "";
312  }
313  #endif
314  #ifdef WINDOWS
315  GetModuleFileName( NULL, Results, FILENAME_MAX );
316  return DirName(String(Results));
317  #endif
318  #ifdef MACOSX
319  uint32_t size = sizeof(Results);
320  if (_NSGetExecutablePath(Results, &size) == 0)
321  {
322  return DirName(String(Results));
323  } else{
324  return "";
325  }
326  #endif
327 
328  }
329 
330  String ResourceManager::GetExecutableDir(int ArgCount, char** ArgVars)
331  {
332  String Results(GetExecutableDirFromArg(ArgCount,ArgVars));
333  if(String(".")==Results || String("")==Results) // Means it might have valid exename
334  {
336  }else{
337  return Results;
338  }
339  }
340 
342  { return GetExecutableDir(ArgC,ArgV); }
343 
345  {
346  #ifdef WINDOWS
347  if(_chdir(ChangeTo.c_str()))
348  #else
349  if(chdir(ChangeTo.c_str()))
350  #endif
351  { MEZZ_EXCEPTION(Exception::IO_DIRECTORY_NOT_FOUND_EXCEPTION,String("Could not change to directory \"")+ChangeTo+"\" error: "+ToString(errno)); }
352  }
353 
354 
355  ///////////////////////////////////////////////////////////////////////////////
356  // Directory Management
357 
358  bool ResourceManager::CreateDirectoryPath(const String& DirectoryPath)
359  {
360  bool Result = true;
361  StringVector FolderNames;
362  CountedPtr<StringVector> FolderVec = StringTools::Split(DirectoryPath,"/\\");
363  size_t StartIndex = 0;
364  String PathAttempt;
365  char SysSlash = GetDirectorySeparator();
366  #ifdef WINDOWS
367  // For windows and windows like machines, see if the first entry is a drive, because attempting to make a drive is silly.
368  if(FolderVec->at(0).find(':') != String::npos)
369  {
370  PathAttempt.append( FolderVec->at(0) );
371  PathAttempt.append( 1, SysSlash );
372  StartIndex++;
373  }
374  #else
375  PathAttempt.append( 1, SysSlash );
376  #endif
377  for( size_t VecIndex = StartIndex ; VecIndex < FolderVec->size() ; ++VecIndex )
378  {
379  PathAttempt.append( FolderVec->at(VecIndex) );
380  PathAttempt.append( 1, SysSlash );
381  Result = this->CreateDirectory( PathAttempt );
382  }
383  return Result;
384  }
385 
387  {
388  StringSet Results;
389  DIR *Directory;
390  struct dirent *DirEntry;
391  if((Directory = opendir(Dir.c_str())))
392  {
393  while((DirEntry = readdir(Directory)))
394  {
395  Results.insert(DirEntry->d_name);
396  //DirEntry->d_type Later this can be modified to include the type of file entry it is, like a file, block device, directory, socket etc...
397  }
398 
399  closedir(Directory);
400  return Results;
401  }else{
402  MEZZ_EXCEPTION(Exception::IO_DIRECTORY_NOT_FOUND_EXCEPTION,String("Error listing directory contents"));
403  }
404  }
405 
407  {
408  char cCurrentPath[FILENAME_MAX];
409  // ©har cCurrentPath[MAXPATHLEN];
410  #ifdef WINDOWS
411  String Results (_getcwd(cCurrentPath,sizeof(cCurrentPath)));
412  #else
413  String Results (getcwd(cCurrentPath,sizeof(cCurrentPath)));
414  #endif
415  return Results;
416  }
417 
419  {
420  return EngineDataDir;
421  }
422 
424  {
425  String LowerVar = PathVar;
426  StringTools::ToLowerCase(LowerVar);
427  if(LowerVar == "localappdata") return GetLocalAppDataDir();
428  else if(LowerVar == "shareableappdata") return GetShareableAppDataDir();
429  else if(LowerVar == "currentuserdata") return GetCurrentUserDataDir();
430  else if(LowerVar == "commonuserdata") return GetCommonUserDataDir();
431  else
432  {
433  MEZZ_EXCEPTION(Exception::PARAMETERS_EXCEPTION,"Attempting to retrieve unknown path variable: \"" + PathVar + "\".");
434  }
435  }
436 
438  {
439  #ifdef WINDOWS
440  TCHAR path_local_appdata[MAX_PATH];
441  if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, path_local_appdata))) {
442  return path_local_appdata;
443  }
444  #else
445  struct passwd* pw = getpwuid(getuid());
446  if(pw) {
447  return String(pw->pw_dir);
448  }else{
449  MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION,"Could not get user information to retrieve app data directory.");
450  }
451 
452  // might be some useful MAC OS X code
453  /*#elif MACOSX
454  FSRef ref;
455  OSType folderType = kApplicationSupportFolderType;
456  char path[PATH_MAX];
457  FSFindFolder( kUserDomain, folderType, kCreateFolder, &ref );
458  FSRefMakePath( &ref, (UInt8*)&path, PATH_MAX );
459  return path;*/
460  #endif
461  return "";
462  }
463 
465  {
466  #ifdef WINDOWS
467  TCHAR path_appdata[MAX_PATH];
468  if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, path_appdata))) {
469  return path_appdata;
470  }
471  #else
472  struct passwd* pw = getpwuid(getuid());
473  if(pw) {
474  return String(pw->pw_dir);
475  }else{
476  MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION,"Could not get user information to retrieve home directory.");
477  }
478  #endif
479  return "";
480  }
481 
483  {
484  #ifdef WINDOWS
485  TCHAR path_personal[MAX_PATH];
486  if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL, 0, path_personal))) {
487  return path_personal;
488  }
489  #else
490  struct passwd* pw = getpwuid(getuid());
491  if(pw) {
492  return String(pw->pw_dir);
493  }else{
494  MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION,"Could not get user information to retrieve user data directory.");
495  }
496  #endif
497  return "";
498  }
499 
501  {
502  #ifdef WINDOWS
503  TCHAR path_common_personal[MAX_PATH];
504  if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS|CSIDL_FLAG_CREATE, NULL, 0, path_common_personal))) {
505  return path_common_personal;
506  }
507  #else
508  struct passwd* pw = getpwuid(getuid());
509  if(pw) {
510  return String(pw->pw_dir);
511  }else{
512  MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION,"Could not get user information to retrieve common data directory.");
513  }
514  #endif
515  return "";
516  }
517 
518  ///////////////////////////////////////////////////////////////////////////////
519  // Stream Management
520 
522  {
523  /// @todo This entire method is a bit of a hack. When the resource system gets refactored it should go through our archives or whatever equivalent.
524  /// Since we currently have to put up with Ogre's system, we'll use it for now as a hack.
525 
526  NamedDataStreamIterator StreamIt = this->NamedDataStreams.find(AssetName);
527  if( StreamIt != this->NamedDataStreams.end() )
528  return (*StreamIt).second;
529 
530  Ogre::DataStreamPtr OgreStream = this->OgreResource->openResource(AssetName,AssetGroup);
531  Char8* AssetBuffer = new Char8[ OgreStream->size() ];
532  OgreStream->read( (void*)AssetBuffer, OgreStream->size() );
533 
534  return this->CreateDataStream(AssetName,AssetBuffer,OgreStream->size());
535  }
536 
538  {
539  Resource::DataStreamPtr NewStream( new Resource::MemoryStream(Buffer,BufferSize,true) );
540  this->DataStreams.push_back(NewStream);
541  return NewStream;
542  }
543 
544  Resource::DataStreamPtr ResourceManager::CreateDataStream(const String& AssetName, void* Buffer, const UInt32 BufferSize)
545  {
546  Resource::DataStreamPtr NewStream( new Resource::MemoryStream(Buffer,BufferSize,true) );
547  this->NamedDataStreams.insert(std::pair<String,Resource::DataStreamPtr>(AssetName,NewStream));
548  return NewStream;
549  }
550 
551  Resource::DataStreamPtr ResourceManager::CreateDataStream(const String& AssetName, const String& AssetGroup, void* Buffer, const UInt32 BufferSize)
552  {
553  Resource::DataStreamPtr NewStream( new Resource::MemoryStream(Buffer,BufferSize,true) );
554 
555  /// @todo Once we have our own AssetGroup implementation we need to implement this.
556  MEZZ_EXCEPTION(Exception::NOT_IMPLEMENTED_EXCEPTION,"Assigning new DataStreams to AssetGroups has not yet been implemented.");
557 
558  return NewStream;
559  }
560 
561  ///////////////////////////////////////////////////////////////////////////////
562  // AssetGroup Management
563 
565  {
566  for( Whole X = 0 ; X < ResourceGroups.size() ; X++ )
567  {
568  if(Name == ResourceGroups[X])
569  return;
570  }
571  ResourceGroups.push_back(Name);
572  }
573 
574  void ResourceManager::AddAssetLocation(const String& Location, const ArchiveType Type, const String& Group, bool Recursive)
575  {
576  this->OgreResource->addResourceLocation(Location, GetStringFromArchiveType(Type), Group, Recursive);
577  AddAssetGroupName(Group);
578  }
579 
581  {
582  this->OgreResource->createResourceGroup(GroupName);
583  AddAssetGroupName(GroupName);
584  }
585 
587  {
588  for( std::vector<String>::iterator it = ResourceGroups.begin() ; it != ResourceGroups.end() ; it++ )
589  {
590  if(GroupName == (*it))
591  {
592  ResourceGroups.erase(it);
593  break;
594  }
595  }
596  /// @todo This is a bit of a hack, but needs to be here until we can upgrade our resource system.
597  Ogre::StringVectorPtr ResourceNames = this->OgreResource->listResourceNames(GroupName,false);
598  for( Whole X = 0 ; X < ResourceNames->size() ; ++X )
599  {
600  if(ResourceNames->at(X).find(".mesh"))
601  {
602  Graphics::MeshManager::GetSingletonPtr()->UnloadMesh(ResourceNames->at(X));
603  }
604  }
605  this->OgreResource->destroyResourceGroup(GroupName);
606  }
607 
608  void ResourceManager::DeclareAsset(const String& Name, const String& Type, const String& Group)
609  {
610  this->OgreResource->declareResource(Name, Type, Group);
611  }
612 
614  {
615  this->OgreResource->initialiseResourceGroup(Group);
616  }
617 
618  ///////////////////////////////////////////////////////////////////////////////
619  // Resource/Asset Query
620 
621  String ResourceManager::GetAssetPath(const String& FileName, const String& Group)
622  {
623  Ogre::FileInfoListPtr FileList = this->OgreResource->listResourceFileInfo(Group);
624  for( Whole X = 0 ; X < FileList->size() ; ++X )
625  {
626  if( FileName == FileList->at(X).filename ) {
627  //return FileList->at(X).path;
628  return FileList->at(X).archive->getName() + "/" + FileList->at(X).path;
629  }
630  }
631  return "";
632  }
633 
634  ///////////////////////////////////////////////////////////////////////////////
635  // Utility
636 
638  {
639  #ifdef WINDOWS
640  return ".dll";
641  #elif LINUX
642  return ".so";
643  #elif MACOSX
644  return ".dylib";
645  #endif
646  }
647 
649  {
650  #ifdef MEZZDEBUG
651  Entresol::GetSingletonPtr()->Log("Entering ResourceManager::GetResourceStream(const String& FileName)");
652  #endif
653  Internal::OgreDataStreamBuf *TempBuffer = new Internal::OgreDataStreamBuf(OgreResource->openResource(FileName));
654  ResourceInputStream *Results = new ResourceInputStream(TempBuffer, this);
655  this->DeleteList.push_back(Results);
656  #ifdef MEZZDEBUG
657  Entresol::GetSingletonPtr()->Log("Exiting ResourceManager::GetResourceStream(const String& FileName)");
658  #endif
659  return Results;
660  }
661 
663  {
664  Initialized = true;
665  }
666 
668  {
669  Initialized = false;
670  }
671 
673  {
674  switch(ArchType)
675  {
677  return String("FileSystem");
678  case Mezzanine::AT_Zip:
679  return String("Zip");
680  default:
681  MEZZ_EXCEPTION(Exception::PARAMETERS_EXCEPTION, "Invalid archive type passed to ResourceManager::GetStringFromArchiveType.");
682  return String("");
683  }
684  }
685 
687  {
688  if(String("FileSystem")==FromString)
689  { return Mezzanine::AT_FileSystem; }
690  if(String("Zip")==FromString)
691  { return Mezzanine::AT_Zip; }
692  MEZZ_EXCEPTION(Exception::PARAMETERS_EXCEPTION, "Invalid archive type passed to ResourceManager::GetArchiveTypeFromString.");
693  return AT_Invalid;
694  }
695 
696  ///////////////////////////////////////////////////////////////////////////////
697  // Type Identifier Methods
698 
700  { return ManagerBase::MT_ResourceManager; }
701 
703  { return "DefaultResourceManager"; }
704 
705  ///////////////////////////////////////////////////////////////////////////////
706  // DefaultResourceManagerFactory Methods
707 
709  {
710  }
711 
713  {
714  }
715 
717  {
718  return "DefaultResourceManager";
719  }
720 
722  {
724  {
725  /// @todo Add something to log a warning that the manager exists and was requested to be constructed when we have a logging manager set up.
727  }else{
728  if(Params.empty()) return new ResourceManager();
729  else
730  {
731  String EngineDataPath;
732  ArchiveType ArchiveType_;
733  for( NameValuePairList::iterator ParIt = Params.begin() ; ParIt != Params.end() ; ++ParIt )
734  {
735  String Lower = (*ParIt).first;
737  if( "enginedatapath" == Lower )
738  {
739  EngineDataPath = (*ParIt).second;
740  }
741  else if( "archivetype" == Lower )
742  {
743  ArchiveType_ = ResourceManager::GetArchiveTypeFromString((*ParIt).second);
744  }
745  }
746  return new ResourceManager(EngineDataPath,ArchiveType_);
747  }
748  }
749  }
750 
752  {
754  {
755  /// @todo Add something to log a warning that the manager exists and was requested to be constructed when we have a logging manager set up.
757  }else return new ResourceManager(XMLNode);
758  }
759 
761  {
762  delete ToBeDestroyed;
763  }
764 }//Mezzanine
765 
766 #endif