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