1 /* Copyright (C) 2015 Wildfire Games.
2 * This file is part of 0 A.D.
3 *
4 * 0 A.D. 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 2 of the License, or
7 * (at your option) any later version.
8 *
9 * 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "precompiled.h"
19 #include "Filesystem.h"
20
21 #include "ps/CLogger.h"
22 #include "ps/Profile.h"
23
24 #include "lib/res/h_mgr.h" // h_reload
25 #include "lib/sysdep/dir_watch.h"
26 #include "lib/utf8.h"
27
28
29 PIVFS g_VFS;
30
31 static std::vector<std::pair<FileReloadFunc, void*> > g_ReloadFuncs;
32
VfsFileExists(const VfsPath & pathname)33 bool VfsFileExists(const VfsPath& pathname)
34 {
35 return g_VFS->GetFileInfo(pathname, 0) == INFO::OK;
36 }
37
VfsDirectoryExists(const VfsPath & pathname)38 bool VfsDirectoryExists(const VfsPath& pathname)
39 {
40 ENSURE(pathname.IsDirectory());
41 return g_VFS->GetDirectoryEntries(pathname, NULL, NULL) == INFO::OK;
42 }
43
RegisterFileReloadFunc(FileReloadFunc func,void * obj)44 void RegisterFileReloadFunc(FileReloadFunc func, void* obj)
45 {
46 g_ReloadFuncs.emplace_back(func, obj);
47 }
48
UnregisterFileReloadFunc(FileReloadFunc func,void * obj)49 void UnregisterFileReloadFunc(FileReloadFunc func, void* obj)
50 {
51 g_ReloadFuncs.erase(std::remove(g_ReloadFuncs.begin(), g_ReloadFuncs.end(), std::make_pair(func, obj)));
52 }
53
54 // try to skip unnecessary work by ignoring uninteresting notifications.
CanIgnore(const DirWatchNotification & notification)55 static bool CanIgnore(const DirWatchNotification& notification)
56 {
57 // ignore directories
58 const OsPath& pathname = notification.Pathname();
59 if(pathname.IsDirectory())
60 return true;
61
62 // ignore uninteresting file types (e.g. temp files, or the
63 // hundreds of XMB files that are generated from XML)
64 const OsPath extension = pathname.Extension();
65 const wchar_t* extensionsToIgnore[] = { L".xmb", L".tmp" };
66 for(size_t i = 0; i < ARRAY_SIZE(extensionsToIgnore); i++)
67 {
68 if(extension == extensionsToIgnore[i])
69 return true;
70 }
71
72 return false;
73 }
74
ReloadChangedFiles()75 Status ReloadChangedFiles()
76 {
77 PROFILE3("hotload");
78
79 std::vector<DirWatchNotification> notifications;
80 RETURN_STATUS_IF_ERR(dir_watch_Poll(notifications));
81 for(size_t i = 0; i < notifications.size(); i++)
82 {
83 if(!CanIgnore(notifications[i]))
84 {
85 VfsPath pathname;
86 RETURN_STATUS_IF_ERR(g_VFS->GetVirtualPath(notifications[i].Pathname(), pathname));
87 RETURN_STATUS_IF_ERR(g_VFS->RemoveFile(pathname));
88 RETURN_STATUS_IF_ERR(g_VFS->RepopulateDirectory(pathname.Parent()/""));
89
90 // Tell each hotloadable system about this file change:
91
92 for (size_t j = 0; j < g_ReloadFuncs.size(); ++j)
93 g_ReloadFuncs[j].first(g_ReloadFuncs[j].second, pathname);
94
95 RETURN_STATUS_IF_ERR(h_reload(g_VFS, pathname));
96 }
97 }
98 return INFO::OK;
99 }
100
GetWstringFromWpath(const fs::wpath & path)101 std::wstring GetWstringFromWpath(const fs::wpath& path)
102 {
103 #if BOOST_FILESYSTEM_VERSION == 3
104 return path.wstring();
105 #else
106 return path.string();
107 #endif
108 }
109
110
CVFSFile()111 CVFSFile::CVFSFile()
112 : m_BufferSize(0)
113 {
114 }
115
~CVFSFile()116 CVFSFile::~CVFSFile()
117 {
118 }
119
Load(const PIVFS & vfs,const VfsPath & filename,bool log)120 PSRETURN CVFSFile::Load(const PIVFS& vfs, const VfsPath& filename, bool log /* = true */)
121 {
122 // Load should never be called more than once, so complain
123 if (m_Buffer)
124 {
125 DEBUG_WARN_ERR(ERR::LOGIC);
126 return PSRETURN_CVFSFile_AlreadyLoaded;
127 }
128
129 Status ret = vfs->LoadFile(filename, m_Buffer, m_BufferSize);
130 if (ret != INFO::OK)
131 {
132 if (log)
133 LOGERROR("CVFSFile: file %s couldn't be opened (vfs_load: %lld)", filename.string8(), (long long)ret);
134 m_Buffer.reset();
135 m_BufferSize = 0;
136 return PSRETURN_CVFSFile_LoadFailed;
137 }
138
139 return PSRETURN_OK;
140 }
141
GetBuffer() const142 const u8* CVFSFile::GetBuffer() const
143 {
144 return m_Buffer.get();
145 }
146
GetBufferSize() const147 size_t CVFSFile::GetBufferSize() const
148 {
149 return m_BufferSize;
150 }
151
GetAsString() const152 CStr CVFSFile::GetAsString() const
153 {
154 return std::string((char*)GetBuffer(), GetBufferSize());
155 }
156
DecodeUTF8() const157 CStr CVFSFile::DecodeUTF8() const
158 {
159 const u8* buffer = GetBuffer();
160
161 // Detect if there's a UTF-8 BOM and strip it
162 if (GetBufferSize() >= 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF)
163 {
164 return std::string(&buffer[3], buffer + GetBufferSize());
165 }
166 else
167 {
168 return std::string(buffer, buffer + GetBufferSize());
169 }
170 }
171