1 /*
2  * This file is part of the Colobot: Gold Edition source code
3  * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4  * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see http://gnu.org/licenses
18  */
19 
20 
21 #include "app/pathman.h"
22 
23 #include "common/config.h"
24 
25 #include "app/app.h"
26 
27 
28 #include "common/logger.h"
29 
30 #include "common/resources/resourcemanager.h"
31 
32 #include "common/system/system.h"
33 #ifdef PLATFORM_WINDOWS
34     #include "common/system/system_windows.h"
35 #endif
36 
37 #include <boost/algorithm/string.hpp>
38 #include <boost/filesystem.hpp>
39 
CPathManager(CSystemUtils * systemUtils)40 CPathManager::CPathManager(CSystemUtils* systemUtils)
41     : m_dataPath(systemUtils->GetDataPath())
42     , m_langPath(systemUtils->GetLangPath())
43     , m_savePath(systemUtils->GetSaveDir())
44     , m_modSearchDirs{}
45 {
46 }
47 
~CPathManager()48 CPathManager::~CPathManager()
49 {
50 }
51 
SetDataPath(const std::string & dataPath)52 void CPathManager::SetDataPath(const std::string &dataPath)
53 {
54     m_dataPath = dataPath;
55 }
56 
SetLangPath(const std::string & langPath)57 void CPathManager::SetLangPath(const std::string &langPath)
58 {
59     m_langPath = langPath;
60 }
61 
SetSavePath(const std::string & savePath)62 void CPathManager::SetSavePath(const std::string &savePath)
63 {
64     m_savePath = savePath;
65 }
66 
GetDataPath()67 const std::string& CPathManager::GetDataPath()
68 {
69     return m_dataPath;
70 }
71 
GetLangPath()72 const std::string& CPathManager::GetLangPath()
73 {
74     return m_langPath;
75 }
76 
GetSavePath()77 const std::string& CPathManager::GetSavePath()
78 {
79     return m_savePath;
80 }
81 
VerifyPaths()82 std::string CPathManager::VerifyPaths()
83 {
84     #if PLATFORM_WINDOWS
85     boost::filesystem::path dataPath(CSystemUtilsWindows::UTF8_Decode(m_dataPath));
86     #else
87     boost::filesystem::path dataPath(m_dataPath);
88     #endif
89     if (! (boost::filesystem::exists(dataPath) && boost::filesystem::is_directory(dataPath)) )
90     {
91         GetLogger()->Error("Data directory '%s' doesn't exist or is not a directory\n", m_dataPath.c_str());
92         return std::string("Could not read from data directory:\n") +
93             std::string("'") + m_dataPath + std::string("'\n") +
94             std::string("Please check your installation, or supply a valid data directory by -datadir option.");
95     }
96 
97     #if PLATFORM_WINDOWS
98     boost::filesystem::path langPath(CSystemUtilsWindows::UTF8_Decode(m_langPath));
99     #else
100     boost::filesystem::path langPath(m_langPath);
101     #endif
102     if (! (boost::filesystem::exists(langPath) && boost::filesystem::is_directory(langPath)) )
103     {
104         GetLogger()->Warn("Language path '%s' is invalid, assuming translation files not installed\n", m_langPath.c_str());
105     }
106 
107     #if PLATFORM_WINDOWS
108     boost::filesystem::create_directories(CSystemUtilsWindows::UTF8_Decode(m_savePath));
109     boost::filesystem::create_directories(CSystemUtilsWindows::UTF8_Decode(m_savePath+"/mods"));
110     #else
111     boost::filesystem::create_directories(m_savePath);
112     boost::filesystem::create_directories(m_savePath+"/mods");
113     #endif
114 
115     return "";
116 }
117 
InitPaths()118 void CPathManager::InitPaths()
119 {
120     GetLogger()->Info("Data path: %s\n", m_dataPath.c_str());
121     GetLogger()->Info("Save path: %s\n", m_savePath.c_str());
122 
123     m_modSearchDirs.push_back(m_dataPath + "/mods");
124     m_modSearchDirs.push_back(m_savePath + "/mods");
125 
126     if (!m_modSearchDirs.empty())
127     {
128         GetLogger()->Info("Mod search dirs:\n");
129         for(const std::string& modSearchDir : m_modSearchDirs)
130             GetLogger()->Info("  * %s\n", modSearchDir.c_str());
131     }
132 
133     CResourceManager::AddLocation(m_dataPath);
134 
135     CResourceManager::SetSaveLocation(m_savePath);
136     CResourceManager::AddLocation(m_savePath);
137 
138     GetLogger()->Debug("Finished initalizing data paths\n");
139     GetLogger()->Debug("PHYSFS search path is:\n");
140     for (const std::string& path : CResourceManager::GetLocations())
141         GetLogger()->Debug("  * %s\n", path.c_str());
142 }
143 
AddMod(const std::string & path)144 void CPathManager::AddMod(const std::string &path)
145 {
146     m_mods.push_back(path);
147 }
148 
FindMods() const149 std::vector<std::string> CPathManager::FindMods() const
150 {
151     std::vector<std::string> mods;
152     GetLogger()->Info("Found mods:\n");
153     for (const auto &searchPath : m_modSearchDirs)
154     {
155         for (const auto &modPath : FindModsInDir(searchPath))
156         {
157             GetLogger()->Info("  * %s\n", modPath.c_str());
158             mods.push_back(modPath);
159         }
160     }
161     GetLogger()->Info("Additional mod paths:\n");
162     for (const auto& modPath : m_mods)
163     {
164         if (boost::filesystem::exists(modPath))
165         {
166             GetLogger()->Info("  * %s\n", modPath.c_str());
167             mods.push_back(modPath);
168         }
169         else
170         {
171             GetLogger()->Warn("Mod does not exist: %s\n", modPath.c_str());
172         }
173     }
174     return mods;
175 }
176 
AddModSearchDir(const std::string & modSearchDirPath)177 void CPathManager::AddModSearchDir(const std::string &modSearchDirPath)
178 {
179     m_modSearchDirs.push_back(modSearchDirPath);
180 }
181 
FindModsInDir(const std::string & dir) const182 std::vector<std::string> CPathManager::FindModsInDir(const std::string &dir) const
183 {
184     std::vector<std::string> ret;
185     try
186     {
187         #if PLATFORM_WINDOWS
188         boost::filesystem::directory_iterator iterator(CSystemUtilsWindows::UTF8_Decode(dir));
189         #else
190         boost::filesystem::directory_iterator iterator(dir);
191         #endif
192         for(; iterator != boost::filesystem::directory_iterator(); ++iterator)
193         {
194             #if PLATFORM_WINDOWS
195             ret.push_back(CSystemUtilsWindows::UTF8_Encode(iterator->path().wstring()));
196             #else
197             ret.push_back(iterator->path().string());
198             #endif
199         }
200     }
201     catch (std::exception &e)
202     {
203         GetLogger()->Warn("Unable to load mods from directory '%s': %s\n", dir.c_str(), e.what());
204     }
205     return ret;
206 }
207