/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "Filesystem.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "lib/res/h_mgr.h" // h_reload
#include "lib/sysdep/dir_watch.h"
#include "lib/utf8.h"
PIVFS g_VFS;
static std::vector > g_ReloadFuncs;
bool VfsFileExists(const VfsPath& pathname)
{
return g_VFS->GetFileInfo(pathname, 0) == INFO::OK;
}
bool VfsDirectoryExists(const VfsPath& pathname)
{
ENSURE(pathname.IsDirectory());
return g_VFS->GetDirectoryEntries(pathname, NULL, NULL) == INFO::OK;
}
void RegisterFileReloadFunc(FileReloadFunc func, void* obj)
{
g_ReloadFuncs.emplace_back(func, obj);
}
void UnregisterFileReloadFunc(FileReloadFunc func, void* obj)
{
g_ReloadFuncs.erase(std::remove(g_ReloadFuncs.begin(), g_ReloadFuncs.end(), std::make_pair(func, obj)));
}
// try to skip unnecessary work by ignoring uninteresting notifications.
static bool CanIgnore(const DirWatchNotification& notification)
{
// ignore directories
const OsPath& pathname = notification.Pathname();
if(pathname.IsDirectory())
return true;
// ignore uninteresting file types (e.g. temp files, or the
// hundreds of XMB files that are generated from XML)
const OsPath extension = pathname.Extension();
const wchar_t* extensionsToIgnore[] = { L".xmb", L".tmp" };
for(size_t i = 0; i < ARRAY_SIZE(extensionsToIgnore); i++)
{
if(extension == extensionsToIgnore[i])
return true;
}
return false;
}
Status ReloadChangedFiles()
{
PROFILE3("hotload");
std::vector notifications;
RETURN_STATUS_IF_ERR(dir_watch_Poll(notifications));
for(size_t i = 0; i < notifications.size(); i++)
{
if(!CanIgnore(notifications[i]))
{
VfsPath pathname;
RETURN_STATUS_IF_ERR(g_VFS->GetVirtualPath(notifications[i].Pathname(), pathname));
RETURN_STATUS_IF_ERR(g_VFS->RemoveFile(pathname));
RETURN_STATUS_IF_ERR(g_VFS->RepopulateDirectory(pathname.Parent()/""));
// Tell each hotloadable system about this file change:
for (size_t j = 0; j < g_ReloadFuncs.size(); ++j)
g_ReloadFuncs[j].first(g_ReloadFuncs[j].second, pathname);
RETURN_STATUS_IF_ERR(h_reload(g_VFS, pathname));
}
}
return INFO::OK;
}
std::wstring GetWstringFromWpath(const fs::wpath& path)
{
#if BOOST_FILESYSTEM_VERSION == 3
return path.wstring();
#else
return path.string();
#endif
}
CVFSFile::CVFSFile()
: m_BufferSize(0)
{
}
CVFSFile::~CVFSFile()
{
}
PSRETURN CVFSFile::Load(const PIVFS& vfs, const VfsPath& filename, bool log /* = true */)
{
// Load should never be called more than once, so complain
if (m_Buffer)
{
DEBUG_WARN_ERR(ERR::LOGIC);
return PSRETURN_CVFSFile_AlreadyLoaded;
}
Status ret = vfs->LoadFile(filename, m_Buffer, m_BufferSize);
if (ret != INFO::OK)
{
if (log)
LOGERROR("CVFSFile: file %s couldn't be opened (vfs_load: %lld)", filename.string8(), (long long)ret);
m_Buffer.reset();
m_BufferSize = 0;
return PSRETURN_CVFSFile_LoadFailed;
}
return PSRETURN_OK;
}
const u8* CVFSFile::GetBuffer() const
{
return m_Buffer.get();
}
size_t CVFSFile::GetBufferSize() const
{
return m_BufferSize;
}
CStr CVFSFile::GetAsString() const
{
return std::string((char*)GetBuffer(), GetBufferSize());
}
CStr CVFSFile::DecodeUTF8() const
{
const u8* buffer = GetBuffer();
// Detect if there's a UTF-8 BOM and strip it
if (GetBufferSize() >= 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF)
{
return std::string(&buffer[3], buffer + GetBufferSize());
}
else
{
return std::string(buffer, buffer + GetBufferSize());
}
}