fixes and improvements
- directoryPosix: replace most methods with boost filesystem (but not all: the latter cannot efficiently enumerate files AND query their size/mtime) - AllocatorChecker: better name for member functions - file: move the File class here - trace: bugfix - io: move UnalignedWriter to write_buffer.cpp (basically the same thing) - vfs: remove unnecessary "vfs" warts from variable names - vfs_tree: VfsFile now stores single Name/Size/MTime fields instead of the FileInfo record (less clunky) - vfs_path: use boost filesystem's version of the basename/extension functions - lf_alloc: remove (no longer necessary, won't be finished - not worth the trouble) - path_util: remove path_foreach_component (replaced by better path traversal logic) and PathPackage (obsoleted by fs::path) ! resource loading code now receives VfsPath as its filename. there is also OsPath (native absolute path) and Path (relative to binaries/data) - tex is now independent of file loading code; it just en/decodes in-memory buffers - wdll_ver: clean up, use smart pointer to simplify bailout code - wsdl: remove nonexistent failure path from calc_gamma (cruised by here because SDL_SetGamme is failing once after a cold boot at work) - wsnd: simplify OpenAL DLL search, use boost::filesystem - wutil: Wow64 redirection is now packaged in a (RAII) class This was SVN commit r5525.
This commit is contained in:
parent
e2d2789c1b
commit
8667ea74c8
@ -38,8 +38,8 @@ class TestMeshManager : public CxxTest::TestSuite
|
||||
directoryPosix.DeleteDirectory(MOD_PATH);
|
||||
directoryPosix.DeleteDirectory(CACHE_PATH);
|
||||
|
||||
TS_ASSERT_OK(directoryPosix.CreateDirectory(MOD_PATH));
|
||||
TS_ASSERT_OK(directoryPosix.CreateDirectory(CACHE_PATH));
|
||||
TS_ASSERT_OK(fs::create_directory(MOD_PATH));
|
||||
TS_ASSERT_OK(fs::create_directory(CACHE_PATH));
|
||||
|
||||
vfs = CreateVfs();
|
||||
|
||||
|
@ -311,21 +311,21 @@ private:
|
||||
class AllocatorChecker
|
||||
{
|
||||
public:
|
||||
void notify_alloc(void* p, size_t size)
|
||||
void OnAllocate(void* p, size_t size)
|
||||
{
|
||||
const Allocs::value_type item = std::make_pair(p, size);
|
||||
std::pair<Allocs::iterator, bool> ret = allocs.insert(item);
|
||||
debug_assert(ret.second == true); // wasn't already in map
|
||||
}
|
||||
|
||||
void notify_free(void* p, size_t size)
|
||||
void OnDeallocate(void* p, size_t size)
|
||||
{
|
||||
Allocs::iterator it = allocs.find(p);
|
||||
if(it == allocs.end())
|
||||
debug_assert(0); // freeing invalid pointer
|
||||
else
|
||||
{
|
||||
// size must match what was passed to notify_alloc
|
||||
// size must match what was passed to OnAllocate
|
||||
const size_t allocated_size = it->second;
|
||||
debug_assert(size == allocated_size);
|
||||
|
||||
@ -336,7 +336,7 @@ public:
|
||||
/**
|
||||
* allocator is resetting itself, i.e. wiping out all allocs.
|
||||
**/
|
||||
void notify_clear()
|
||||
void OnClear()
|
||||
{
|
||||
allocs.clear();
|
||||
}
|
||||
|
@ -23,10 +23,12 @@
|
||||
#include "archive.h"
|
||||
#include "codec_zlib.h"
|
||||
#include "stream.h"
|
||||
#include "lib/file/file.h"
|
||||
#include "lib/file/file_system_posix.h"
|
||||
#include "lib/file/io/io.h"
|
||||
#include "lib/file/io/write_buffer.h"
|
||||
|
||||
static FileSystem_Posix s_posixDirectory;
|
||||
static FileSystem_Posix s_fileSystemPosix;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -359,7 +361,7 @@ public:
|
||||
m_file->Open(pathname, 'r');
|
||||
|
||||
FileInfo fileInfo;
|
||||
s_posixDirectory.GetFileInfo(pathname, &fileInfo);
|
||||
s_fileSystemPosix.GetFileInfo(pathname, &fileInfo);
|
||||
m_fileSize = fileInfo.Size();
|
||||
debug_assert(m_fileSize >= sizeof(LFH)+sizeof(CDFH)+sizeof(ECDR));
|
||||
}
|
||||
@ -524,7 +526,7 @@ public:
|
||||
LibError AddFile(const Path& pathname)
|
||||
{
|
||||
FileInfo fileInfo;
|
||||
RETURN_ERR(s_posixDirectory.GetFileInfo(pathname, &fileInfo));
|
||||
RETURN_ERR(s_fileSystemPosix.GetFileInfo(pathname, &fileInfo));
|
||||
const off_t usize = fileInfo.Size();
|
||||
// skip 0-length files.
|
||||
// rationale: zip.cpp needs to determine whether a CDFH entry is
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "real_directory.h"
|
||||
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/file/file.h"
|
||||
#include "lib/file/io/io.h"
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ TraceEntry::TraceEntry(const char* text)
|
||||
const char* fmt = "%f: %c \"" STRINGIZE(PATH_MAX) "[^\"]\" %d\n";
|
||||
char pathname[PATH_MAX];
|
||||
char action;
|
||||
const int fieldsRead = sscanf_s(text, fmt, &m_timestamp, &m_action, pathname, &m_size);
|
||||
const int fieldsRead = sscanf_s(text, fmt, &m_timestamp, &action, pathname, &m_size);
|
||||
debug_assert(fieldsRead == 4);
|
||||
debug_assert(action == 'L' || action == 'S');
|
||||
m_action = (EAction)action;
|
||||
|
124
source/lib/file/file.cpp
Normal file
124
source/lib/file/file.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : file.cpp
|
||||
* Project : 0 A.D.
|
||||
* Description : simple POSIX file wrapper.
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "file.h"
|
||||
|
||||
#include "lib/file/common/file_stats.h"
|
||||
#include "lib/file/path.h"
|
||||
|
||||
|
||||
ERROR_ASSOCIATE(ERR::FILE_ACCESS, "Insufficient access rights to open file", EACCES);
|
||||
ERROR_ASSOCIATE(ERR::IO, "Error during IO", EIO);
|
||||
|
||||
|
||||
File::File()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
File::~File()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
LibError File::Open(const Path& pathname, char mode)
|
||||
{
|
||||
debug_assert(mode == 'w' || mode == 'r');
|
||||
|
||||
m_pathname = pathname;
|
||||
m_mode = mode;
|
||||
|
||||
int oflag = (mode == 'r')? O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC;
|
||||
#if OS_WIN
|
||||
oflag |= O_BINARY_NP;
|
||||
#endif
|
||||
m_fd = open(m_pathname.external_file_string().c_str(), oflag, S_IRWXO|S_IRWXU|S_IRWXG);
|
||||
if(m_fd < 0)
|
||||
WARN_RETURN(ERR::FILE_ACCESS);
|
||||
|
||||
stats_open();
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
void File::Close()
|
||||
{
|
||||
m_mode = '\0';
|
||||
|
||||
close(m_fd);
|
||||
m_fd = 0;
|
||||
}
|
||||
|
||||
|
||||
LibError File::Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const
|
||||
{
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.aio_lio_opcode = (m_mode == 'w')? LIO_WRITE : LIO_READ;
|
||||
req.aio_buf = (volatile void*)alignedBuf;
|
||||
req.aio_fildes = m_fd;
|
||||
req.aio_offset = alignedOfs;
|
||||
req.aio_nbytes = alignedSize;
|
||||
struct sigevent* sig = 0; // no notification signal
|
||||
aiocb* const reqs = &req;
|
||||
if(lio_listio(LIO_NOWAIT, &reqs, 1, sig) != 0)
|
||||
return LibError_from_errno();
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ LibError File::WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize)
|
||||
{
|
||||
// wait for transfer to complete.
|
||||
while(aio_error(&req) == EINPROGRESS)
|
||||
{
|
||||
aiocb* const reqs = &req;
|
||||
aio_suspend(&reqs, 1, (timespec*)0); // wait indefinitely
|
||||
}
|
||||
|
||||
const ssize_t bytesTransferred = aio_return(&req);
|
||||
if(bytesTransferred == -1) // transfer failed
|
||||
WARN_RETURN(ERR::IO);
|
||||
|
||||
alignedBuf = (u8*)req.aio_buf; // cast from volatile void*
|
||||
alignedSize = bytesTransferred;
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
LibError File::IO(off_t ofs, u8* buf, size_t size) const
|
||||
{
|
||||
ScopedIoMonitor monitor;
|
||||
|
||||
lseek(m_fd, ofs, SEEK_SET);
|
||||
|
||||
errno = 0;
|
||||
const ssize_t ret = (m_mode == 'w')? write(m_fd, buf, size) : read(m_fd, buf, size);
|
||||
if(ret < 0)
|
||||
return LibError_from_errno();
|
||||
|
||||
const size_t totalTransferred = (size_t)ret;
|
||||
if(totalTransferred != size)
|
||||
WARN_RETURN(ERR::IO);
|
||||
|
||||
monitor.NotifyOfSuccess(FI_LOWIO, m_mode, totalTransferred);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
LibError File::Write(off_t ofs, const u8* buf, size_t size) const
|
||||
{
|
||||
return IO(ofs, const_cast<u8*>(buf), size);
|
||||
}
|
||||
|
||||
|
||||
LibError File::Read(off_t ofs, u8* buf, size_t size) const
|
||||
{
|
||||
return IO(ofs, buf, size);
|
||||
}
|
57
source/lib/file/file.h
Normal file
57
source/lib/file/file.h
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : file.h
|
||||
* Project : 0 A.D.
|
||||
* Description : simple POSIX file wrapper.
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
// license: GPL; see lib/license.txt
|
||||
|
||||
#ifndef INCLUDED_FILE
|
||||
#define INCLUDED_FILE
|
||||
|
||||
#include "path.h"
|
||||
|
||||
namespace ERR
|
||||
{
|
||||
const LibError FILE_ACCESS = -110200;
|
||||
const LibError IO = -110201;
|
||||
}
|
||||
|
||||
class LIB_API File
|
||||
{
|
||||
public:
|
||||
File();
|
||||
~File();
|
||||
|
||||
LibError Open(const Path& pathname, char mode);
|
||||
void Close();
|
||||
|
||||
const Path& Pathname() const
|
||||
{
|
||||
return m_pathname;
|
||||
}
|
||||
|
||||
char Mode() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
LibError Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const;
|
||||
static LibError WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize);
|
||||
|
||||
LibError Read(off_t ofs, u8* buf, size_t size) const;
|
||||
LibError Write(off_t ofs, const u8* buf, size_t size) const;
|
||||
|
||||
private:
|
||||
LibError IO(off_t ofs, u8* buf, size_t size) const;
|
||||
|
||||
Path m_pathname;
|
||||
char m_mode;
|
||||
int m_fd;
|
||||
};
|
||||
|
||||
typedef shared_ptr<File> PFile;
|
||||
|
||||
#endif // #ifndef INCLUDED_FILE
|
@ -47,11 +47,6 @@ static bool IsDummyDirectory(const char* name)
|
||||
return LibError_from_errno();
|
||||
shared_ptr<DIR> osDir(pDir, DirDeleter());
|
||||
|
||||
// (we need absolute paths below; using this instead of path_append avoids
|
||||
// a strlen call for each entry.)
|
||||
PathPackage pp;
|
||||
(void)path_package_set_dir(&pp, path.external_directory_string().c_str());
|
||||
|
||||
for(;;)
|
||||
{
|
||||
errno = 0;
|
||||
@ -76,8 +71,8 @@ static bool IsDummyDirectory(const char* name)
|
||||
#else
|
||||
// .. call regular stat().
|
||||
errno = 0;
|
||||
path_package_append_file(&pp, name);
|
||||
if(stat(pp->path, &s) != 0)
|
||||
const Path pathname(path/name);
|
||||
if(stat(pathname.external_directory_string().c_str(), &s) != 0)
|
||||
return LibError_from_errno();
|
||||
#endif
|
||||
|
||||
@ -111,45 +106,20 @@ LibError FileSystem_Posix::GetFileInfo(const Path& pathname, FileInfo* pfileInfo
|
||||
}
|
||||
|
||||
|
||||
LibError FileSystem_Posix::DeleteFile(const Path& pathname)
|
||||
{
|
||||
errno = 0;
|
||||
if(unlink(pathname.external_file_string().c_str()) != 0)
|
||||
return LibError_from_errno();
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
LibError FileSystem_Posix::CreateDirectory(const Path& path)
|
||||
{
|
||||
errno = 0;
|
||||
if(mkdir(path.external_directory_string().c_str(), S_IRWXO|S_IRWXU|S_IRWXG) != 0)
|
||||
return LibError_from_errno();
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
LibError FileSystem_Posix::DeleteDirectory(const Path& path)
|
||||
{
|
||||
// note: we have to recursively empty the directory before it can
|
||||
// be deleted (required by Windows and POSIX rmdir()).
|
||||
|
||||
const char* osPath = path.external_directory_string().c_str();
|
||||
|
||||
PathPackage pp;
|
||||
RETURN_ERR(path_package_set_dir(&pp, osPath));
|
||||
|
||||
FileInfos files; DirectoryNames subdirectoryNames;
|
||||
RETURN_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames));
|
||||
|
||||
// delete files
|
||||
for(size_t i = 0; i < files.size(); i++)
|
||||
{
|
||||
RETURN_ERR(path_package_append_file(&pp, files[i].Name().c_str()));
|
||||
const Path pathname(path/files[i].Name());
|
||||
errno = 0;
|
||||
if(unlink(pp.path) != 0)
|
||||
if(unlink(pathname.external_file_string().c_str()) != 0)
|
||||
return LibError_from_errno();
|
||||
}
|
||||
|
||||
@ -158,7 +128,7 @@ LibError FileSystem_Posix::DeleteDirectory(const Path& path)
|
||||
RETURN_ERR(DeleteDirectory(path/subdirectoryNames[i]));
|
||||
|
||||
errno = 0;
|
||||
if(rmdir(osPath) != 0)
|
||||
if(rmdir(path.external_directory_string().c_str()) != 0)
|
||||
return LibError_from_errno();
|
||||
|
||||
return INFO::OK;
|
||||
|
@ -15,13 +15,16 @@
|
||||
#include "lib/file/path.h"
|
||||
#include "lib/file/file_system.h"
|
||||
|
||||
// jw 2007-12-20: we'd love to replace this with boost::filesystem,
|
||||
// but basic_directory_iterator does not yet cache file_size and
|
||||
// last_write_time in file_status. (they each entail a stat() call,
|
||||
// which is unacceptably slow.)
|
||||
|
||||
struct FileSystem_Posix
|
||||
{
|
||||
virtual LibError GetFileInfo(const Path& pathname, FileInfo* fileInfo) const;
|
||||
virtual LibError GetDirectoryEntries(const Path& path, FileInfos* files, DirectoryNames* subdirectoryNames) const;
|
||||
|
||||
LibError DeleteFile(const Path& pathname);
|
||||
LibError CreateDirectory(const Path& dirPath);
|
||||
LibError DeleteDirectory(const Path& dirPath);
|
||||
};
|
||||
|
||||
|
@ -65,7 +65,7 @@ void fs_SortDirectories(DirectoryNames& directories)
|
||||
|
||||
LibError fs_ForEachFile(PIVFS fs, const VfsPath& path, FileCallback cb, uintptr_t cbData, const char* pattern, unsigned flags)
|
||||
{
|
||||
debug_assert(IsDirectory(path));
|
||||
debug_assert(vfs_path_IsDirectory(path));
|
||||
|
||||
// (declare here to avoid reallocations)
|
||||
FileInfos files; DirectoryNames subdirectoryNames;
|
||||
|
@ -14,17 +14,12 @@
|
||||
#include "lib/allocators/allocators.h" // AllocatorChecker
|
||||
#include "lib/bits.h" // IsAligned, round_up
|
||||
#include "lib/sysdep/cpu.h" // cpu_memcpy
|
||||
#include "lib/file/file.h"
|
||||
#include "lib/file/common/file_stats.h"
|
||||
#include "lib/file/path.h"
|
||||
#include "block_cache.h"
|
||||
#include "io_internal.h"
|
||||
|
||||
|
||||
ERROR_ASSOCIATE(ERR::FILE_ACCESS, "Insufficient access rights to open file", EACCES);
|
||||
ERROR_ASSOCIATE(ERR::IO, "Error during IO", EIO);
|
||||
ERROR_ASSOCIATE(ERR::IO_EOF, "Reading beyond end of file", -1);
|
||||
|
||||
|
||||
// the underlying aio implementation likes buffer and offset to be
|
||||
// sector-aligned; if not, the transfer goes through an align buffer,
|
||||
// and requires an extra cpu_memcpy.
|
||||
@ -83,7 +78,7 @@ public:
|
||||
{
|
||||
debug_assert(m_paddedSize != 0);
|
||||
#ifndef NDEBUG
|
||||
allocatorChecker.notify_free(mem, m_paddedSize);
|
||||
allocatorChecker.OnDeallocate(mem, m_paddedSize);
|
||||
#endif
|
||||
page_aligned_free(mem, m_paddedSize);
|
||||
m_paddedSize = 0;
|
||||
@ -106,124 +101,13 @@ shared_ptr<u8> io_Allocate(size_t size, off_t ofs)
|
||||
throw std::bad_alloc();
|
||||
|
||||
#ifndef NDEBUG
|
||||
allocatorChecker.notify_alloc(mem, paddedSize);
|
||||
allocatorChecker.OnAllocate(mem, paddedSize);
|
||||
#endif
|
||||
|
||||
return shared_ptr<u8>(mem, IoDeleter(paddedSize));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
File::File()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
File::~File()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
LibError File::Open(const Path& pathname, char mode)
|
||||
{
|
||||
debug_assert(mode == 'w' || mode == 'r');
|
||||
|
||||
m_pathname = pathname;
|
||||
m_mode = mode;
|
||||
|
||||
int oflag = (mode == 'r')? O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC;
|
||||
#if OS_WIN
|
||||
oflag |= O_BINARY_NP;
|
||||
#endif
|
||||
m_fd = open(m_pathname.external_file_string().c_str(), oflag, S_IRWXO|S_IRWXU|S_IRWXG);
|
||||
if(m_fd < 0)
|
||||
WARN_RETURN(ERR::FILE_ACCESS);
|
||||
|
||||
stats_open();
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
void File::Close()
|
||||
{
|
||||
m_mode = '\0';
|
||||
|
||||
close(m_fd);
|
||||
m_fd = 0;
|
||||
}
|
||||
|
||||
|
||||
LibError File::Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const
|
||||
{
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.aio_lio_opcode = (m_mode == 'w')? LIO_WRITE : LIO_READ;
|
||||
req.aio_buf = (volatile void*)alignedBuf;
|
||||
req.aio_fildes = m_fd;
|
||||
req.aio_offset = alignedOfs;
|
||||
req.aio_nbytes = alignedSize;
|
||||
struct sigevent* sig = 0; // no notification signal
|
||||
aiocb* const reqs = &req;
|
||||
if(lio_listio(LIO_NOWAIT, &reqs, 1, sig) != 0)
|
||||
return LibError_from_errno();
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ LibError File::WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize)
|
||||
{
|
||||
// wait for transfer to complete.
|
||||
while(aio_error(&req) == EINPROGRESS)
|
||||
{
|
||||
aiocb* const reqs = &req;
|
||||
aio_suspend(&reqs, 1, (timespec*)0); // wait indefinitely
|
||||
}
|
||||
|
||||
const ssize_t bytesTransferred = aio_return(&req);
|
||||
if(bytesTransferred == -1) // transfer failed
|
||||
WARN_RETURN(ERR::IO);
|
||||
|
||||
alignedBuf = (u8*)req.aio_buf; // cast from volatile void*
|
||||
alignedSize = bytesTransferred;
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
LibError File::IO(off_t ofs, u8* buf, size_t size) const
|
||||
{
|
||||
ScopedIoMonitor monitor;
|
||||
|
||||
lseek(m_fd, ofs, SEEK_SET);
|
||||
|
||||
errno = 0;
|
||||
const ssize_t ret = (m_mode == 'w')? write(m_fd, buf, size) : read(m_fd, buf, size);
|
||||
if(ret < 0)
|
||||
return LibError_from_errno();
|
||||
|
||||
const size_t totalTransferred = (size_t)ret;
|
||||
if(totalTransferred != size)
|
||||
WARN_RETURN(ERR::IO);
|
||||
|
||||
monitor.NotifyOfSuccess(FI_LOWIO, m_mode, totalTransferred);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
LibError File::Write(off_t ofs, const u8* buf, size_t size) const
|
||||
{
|
||||
return IO(ofs, const_cast<u8*>(buf), size);
|
||||
}
|
||||
|
||||
|
||||
LibError File::Read(off_t ofs, u8* buf, size_t size) const
|
||||
{
|
||||
return IO(ofs, buf, size);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// BlockIo
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -438,93 +322,3 @@ LibError io_ReadAligned(const File& file, off_t alignedOfs, u8* alignedBuf, size
|
||||
IoSplitter splitter(alignedOfs, alignedBuf, (off_t)size);
|
||||
return splitter.Run(file);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// UnalignedWriter
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class UnalignedWriter::Impl : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
Impl(const File& file, off_t ofs)
|
||||
: m_file(file), m_alignedBuf(io_Allocate(BLOCK_SIZE))
|
||||
{
|
||||
const size_t misalignment = (size_t)ofs % BLOCK_SIZE;
|
||||
m_alignedOfs = ofs - (off_t)misalignment;
|
||||
if(misalignment)
|
||||
io_ReadAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE);
|
||||
m_bytesUsed = misalignment;
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
|
||||
LibError Append(const u8* data, size_t size) const
|
||||
{
|
||||
while(size != 0)
|
||||
{
|
||||
// optimization: write directly from the input buffer, if possible
|
||||
const size_t alignedSize = (size / BLOCK_SIZE) * BLOCK_SIZE;
|
||||
if(m_bytesUsed == 0 && IsAligned(data, BLOCK_SIZE) && alignedSize != 0)
|
||||
{
|
||||
RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, data, alignedSize));
|
||||
m_alignedOfs += (off_t)alignedSize;
|
||||
data += alignedSize;
|
||||
size -= alignedSize;
|
||||
}
|
||||
|
||||
const size_t chunkSize = std::min(size, BLOCK_SIZE-m_bytesUsed);
|
||||
cpu_memcpy(m_alignedBuf.get()+m_bytesUsed, data, chunkSize);
|
||||
m_bytesUsed += chunkSize;
|
||||
data += chunkSize;
|
||||
size -= chunkSize;
|
||||
|
||||
if(m_bytesUsed == BLOCK_SIZE)
|
||||
RETURN_ERR(WriteBlock());
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
void Flush() const
|
||||
{
|
||||
if(m_bytesUsed)
|
||||
{
|
||||
memset(m_alignedBuf.get()+m_bytesUsed, 0, BLOCK_SIZE-m_bytesUsed);
|
||||
(void)WriteBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
LibError WriteBlock() const
|
||||
{
|
||||
RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE));
|
||||
m_alignedOfs += BLOCK_SIZE;
|
||||
m_bytesUsed = 0;
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
const File& m_file;
|
||||
mutable off_t m_alignedOfs;
|
||||
shared_ptr<u8> m_alignedBuf;
|
||||
mutable size_t m_bytesUsed;
|
||||
};
|
||||
|
||||
|
||||
UnalignedWriter::UnalignedWriter(const File& file, off_t ofs)
|
||||
: impl(new Impl(file, ofs))
|
||||
{
|
||||
}
|
||||
|
||||
LibError UnalignedWriter::Append(const u8* data, size_t size) const
|
||||
{
|
||||
return impl->Append(data, size);
|
||||
}
|
||||
|
||||
void UnalignedWriter::Flush() const
|
||||
{
|
||||
impl->Flush();
|
||||
}
|
||||
|
@ -11,61 +11,14 @@
|
||||
#ifndef INCLUDED_IO
|
||||
#define INCLUDED_IO
|
||||
|
||||
#include "lib/file/path.h"
|
||||
|
||||
namespace ERR
|
||||
{
|
||||
const LibError FILE_ACCESS = -110200;
|
||||
const LibError IO = -110201;
|
||||
const LibError IO_EOF = -110202;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* simple POSIX file wrapper.
|
||||
**/
|
||||
class LIB_API File
|
||||
{
|
||||
public:
|
||||
File();
|
||||
~File();
|
||||
|
||||
LibError Open(const Path& pathname, char mode);
|
||||
void Close();
|
||||
|
||||
const Path& Pathname() const
|
||||
{
|
||||
return m_pathname;
|
||||
}
|
||||
|
||||
char Mode() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
LibError Issue(aiocb& req, off_t alignedOfs, u8* alignedBuf, size_t alignedSize) const;
|
||||
static LibError WaitUntilComplete(aiocb& req, u8*& alignedBuf, size_t& alignedSize);
|
||||
|
||||
LibError Read(off_t ofs, u8* buf, size_t size) const;
|
||||
LibError Write(off_t ofs, const u8* buf, size_t size) const;
|
||||
|
||||
private:
|
||||
LibError IO(off_t ofs, u8* buf, size_t size) const;
|
||||
|
||||
Path m_pathname;
|
||||
char m_mode;
|
||||
int m_fd;
|
||||
};
|
||||
|
||||
typedef shared_ptr<File> PFile;
|
||||
|
||||
|
||||
// memory will be allocated from the heap, not the (limited) file cache.
|
||||
// this makes sense for write buffers that are never used again,
|
||||
// because we avoid having to displace some other cached items.
|
||||
shared_ptr<u8> io_Allocate(size_t size, off_t ofs = 0);
|
||||
|
||||
|
||||
class File;
|
||||
|
||||
/**
|
||||
* called after a block IO has completed.
|
||||
*
|
||||
@ -83,25 +36,4 @@ LIB_API LibError io_Read(const File& file, off_t ofs, u8* alignedBuf, size_t siz
|
||||
LIB_API LibError io_WriteAligned(const File& file, off_t alignedOfs, const u8* alignedData, size_t size);
|
||||
LIB_API LibError io_ReadAligned(const File& file, off_t alignedOfs, u8* alignedBuf, size_t size);
|
||||
|
||||
class LIB_API UnalignedWriter
|
||||
{
|
||||
public:
|
||||
UnalignedWriter(const File& file, off_t ofs);
|
||||
|
||||
/**
|
||||
* add data to the align buffer, writing it out to disk if full.
|
||||
**/
|
||||
LibError Append(const u8* data, size_t size) const;
|
||||
|
||||
/**
|
||||
* zero-initialize any remaining space in the align buffer and write
|
||||
* it to the file. this is called by the destructor.
|
||||
**/
|
||||
void Flush() const;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
shared_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
#endif // #ifndef INCLUDED_IO
|
||||
|
101
source/lib/file/io/write_buffer.cpp
Normal file
101
source/lib/file/io/write_buffer.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include "precompiled.h"
|
||||
#include "write_buffer.h"
|
||||
|
||||
#include "lib/bits.h" // IsAligned
|
||||
#include "lib/sysdep/cpu.h"
|
||||
#include "io.h"
|
||||
#include "io_internal.h"
|
||||
|
||||
|
||||
WriteBuffer::WriteBuffer()
|
||||
: m_capacity(4096), m_data(io_Allocate(m_capacity)), m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void WriteBuffer::Append(const void* data, size_t size)
|
||||
{
|
||||
while(m_size + size > m_capacity)
|
||||
m_capacity *= 2;
|
||||
shared_ptr<u8> newData = io_Allocate(m_capacity);
|
||||
cpu_memcpy(newData.get(), m_data.get(), m_size);
|
||||
m_data = newData;
|
||||
|
||||
cpu_memcpy(m_data.get() + m_size, data, size);
|
||||
m_size += size;
|
||||
}
|
||||
|
||||
|
||||
void WriteBuffer::Overwrite(const void* data, size_t size, size_t offset)
|
||||
{
|
||||
debug_assert(offset+size < m_size);
|
||||
cpu_memcpy(m_data.get()+offset, data, size);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// UnalignedWriter
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
UnalignedWriter::UnalignedWriter(const File& file, off_t ofs)
|
||||
: m_file(file), m_alignedBuf(io_Allocate(BLOCK_SIZE))
|
||||
{
|
||||
const size_t misalignment = (size_t)ofs % BLOCK_SIZE;
|
||||
m_alignedOfs = ofs - (off_t)misalignment;
|
||||
if(misalignment)
|
||||
io_ReadAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE);
|
||||
m_bytesUsed = misalignment;
|
||||
}
|
||||
|
||||
|
||||
UnalignedWriter::~UnalignedWriter()
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
LibError UnalignedWriter::Append(const u8* data, size_t size) const
|
||||
{
|
||||
while(size != 0)
|
||||
{
|
||||
// optimization: write directly from the input buffer, if possible
|
||||
const size_t alignedSize = (size / BLOCK_SIZE) * BLOCK_SIZE;
|
||||
if(m_bytesUsed == 0 && IsAligned(data, BLOCK_SIZE) && alignedSize != 0)
|
||||
{
|
||||
RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, data, alignedSize));
|
||||
m_alignedOfs += (off_t)alignedSize;
|
||||
data += alignedSize;
|
||||
size -= alignedSize;
|
||||
}
|
||||
|
||||
const size_t chunkSize = std::min(size, BLOCK_SIZE-m_bytesUsed);
|
||||
cpu_memcpy(m_alignedBuf.get()+m_bytesUsed, data, chunkSize);
|
||||
m_bytesUsed += chunkSize;
|
||||
data += chunkSize;
|
||||
size -= chunkSize;
|
||||
|
||||
if(m_bytesUsed == BLOCK_SIZE)
|
||||
RETURN_ERR(WriteBlock());
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
void UnalignedWriter::Flush() const
|
||||
{
|
||||
if(m_bytesUsed)
|
||||
{
|
||||
memset(m_alignedBuf.get()+m_bytesUsed, 0, BLOCK_SIZE-m_bytesUsed);
|
||||
(void)WriteBlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LibError UnalignedWriter::WriteBlock() const
|
||||
{
|
||||
RETURN_ERR(io_WriteAligned(m_file, m_alignedOfs, m_alignedBuf.get(), BLOCK_SIZE));
|
||||
m_alignedOfs += BLOCK_SIZE;
|
||||
m_bytesUsed = 0;
|
||||
return INFO::OK;
|
||||
}
|
@ -1,31 +1,15 @@
|
||||
#ifndef INCLUDED_WRITE_BUFFER
|
||||
#define INCLUDED_WRITE_BUFFER
|
||||
|
||||
class File;
|
||||
|
||||
class LIB_API WriteBuffer
|
||||
{
|
||||
public:
|
||||
WriteBuffer()
|
||||
: m_capacity(4096), m_data(io_Allocate(m_capacity)), m_size(0)
|
||||
{
|
||||
}
|
||||
WriteBuffer();
|
||||
|
||||
void Append(const void* data, size_t size)
|
||||
{
|
||||
while(m_size + size > m_capacity)
|
||||
m_capacity *= 2;
|
||||
shared_ptr<u8> newData = io_Allocate(m_capacity);
|
||||
cpu_memcpy(newData.get(), m_data.get(), m_size);
|
||||
m_data = newData;
|
||||
|
||||
cpu_memcpy(m_data.get() + m_size, data, size);
|
||||
m_size += size;
|
||||
}
|
||||
|
||||
void Overwrite(const void* data, size_t size, size_t offset)
|
||||
{
|
||||
debug_assert(offset+size < m_size);
|
||||
cpu_memcpy(m_data.get()+offset, data, size);
|
||||
}
|
||||
void Append(const void* data, size_t size);
|
||||
void Overwrite(const void* data, size_t size, size_t offset);
|
||||
|
||||
shared_ptr<u8> Data() const
|
||||
{
|
||||
@ -44,4 +28,31 @@ private:
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
|
||||
class LIB_API UnalignedWriter : public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
UnalignedWriter(const File& file, off_t ofs);
|
||||
~UnalignedWriter();
|
||||
|
||||
/**
|
||||
* add data to the align buffer, writing it out to disk if full.
|
||||
**/
|
||||
LibError Append(const u8* data, size_t size) const;
|
||||
|
||||
/**
|
||||
* zero-initialize any remaining space in the align buffer and write
|
||||
* it to the file. this is called by the destructor.
|
||||
**/
|
||||
void Flush() const;
|
||||
|
||||
private:
|
||||
LibError WriteBlock() const;
|
||||
|
||||
const File& m_file;
|
||||
shared_ptr<u8> m_alignedBuf;
|
||||
mutable off_t m_alignedOfs;
|
||||
mutable size_t m_bytesUsed;
|
||||
};
|
||||
|
||||
#endif // #ifndef INCLUDED_WRITE_BUFFER
|
||||
|
@ -84,7 +84,7 @@ public:
|
||||
|
||||
u8* mem = (u8*)m_allocator.Allocate(alignedSize);
|
||||
#ifndef NDEBUG
|
||||
m_checker.notify_alloc(mem, alignedSize);
|
||||
m_checker.OnAllocate(mem, alignedSize);
|
||||
#endif
|
||||
|
||||
return shared_ptr<u8>(mem, FileCacheDeleter(size, pthis));
|
||||
@ -100,7 +100,7 @@ public:
|
||||
(void)mprotect(mem, size, PROT_READ|PROT_WRITE);
|
||||
|
||||
#ifndef NDEBUG
|
||||
m_checker.notify_free(mem, alignedSize);
|
||||
m_checker.OnDeallocate(mem, alignedSize);
|
||||
#endif
|
||||
m_allocator.Deallocate(mem, alignedSize);
|
||||
|
||||
|
@ -32,34 +32,34 @@ public:
|
||||
|
||||
virtual LibError Mount(const VfsPath& mountPoint, const char* path, uint flags /* = 0 */, uint priority /* = 0 */)
|
||||
{
|
||||
debug_assert(IsDirectory(mountPoint));
|
||||
debug_assert(vfs_path_IsDirectory(mountPoint));
|
||||
debug_assert(strcmp(path, ".") != 0); // "./" isn't supported on Windows.
|
||||
// note: mounting subdirectoryNames is now allowed.
|
||||
|
||||
VfsDirectory* vfsDirectory;
|
||||
CHECK_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, vfsDirectory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE));
|
||||
VfsDirectory* directory;
|
||||
CHECK_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE));
|
||||
PRealDirectory realDirectory(new RealDirectory(std::string(path), priority, flags));
|
||||
vfsDirectory->Attach(realDirectory);
|
||||
directory->Attach(realDirectory);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
virtual LibError GetFileInfo(const VfsPath& pathname, FileInfo* pfileInfo) const
|
||||
{
|
||||
VfsDirectory* vfsDirectory; VfsFile* vfsFile;
|
||||
LibError ret = vfs_Lookup(pathname, &m_rootDirectory, vfsDirectory, &vfsFile);
|
||||
VfsDirectory* directory; VfsFile* file;
|
||||
LibError ret = vfs_Lookup(pathname, &m_rootDirectory, directory, &file);
|
||||
if(!pfileInfo) // just indicate if the file exists without raising warnings.
|
||||
return ret;
|
||||
CHECK_ERR(ret);
|
||||
vfsFile->GetFileInfo(pfileInfo);
|
||||
*pfileInfo = FileInfo(file->Name(), file->Size(), file->MTime());
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
virtual LibError GetDirectoryEntries(const VfsPath& path, FileInfos* files, DirectoryNames* subdirectoryNames) const
|
||||
{
|
||||
debug_assert(IsDirectory(path));
|
||||
VfsDirectory* vfsDirectory;
|
||||
CHECK_ERR(vfs_Lookup(path, &m_rootDirectory, vfsDirectory, 0));
|
||||
vfsDirectory->GetEntries(files, subdirectoryNames);
|
||||
debug_assert(vfs_path_IsDirectory(path));
|
||||
VfsDirectory* directory;
|
||||
CHECK_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0));
|
||||
directory->GetEntries(files, subdirectoryNames);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
@ -67,15 +67,15 @@ public:
|
||||
// coherency (need only invalidate when closing a FILE_WRITE file).
|
||||
virtual LibError CreateFile(const VfsPath& pathname, shared_ptr<u8> fileContents, size_t size)
|
||||
{
|
||||
VfsDirectory* vfsDirectory;
|
||||
CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, vfsDirectory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE));
|
||||
VfsDirectory* directory;
|
||||
CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE));
|
||||
|
||||
PRealDirectory realDirectory = vfsDirectory->AssociatedDirectory();
|
||||
PRealDirectory realDirectory = directory->AssociatedDirectory();
|
||||
const std::string& name = pathname.leaf();
|
||||
RETURN_ERR(realDirectory->Store(name, fileContents, size));
|
||||
|
||||
const VfsFile file(FileInfo(name, (off_t)size, time(0)), realDirectory->Priority(), realDirectory);
|
||||
vfsDirectory->AddFile(file);
|
||||
const VfsFile file(name, (off_t)size, time(0), realDirectory->Priority(), realDirectory);
|
||||
directory->AddFile(file);
|
||||
|
||||
// wipe out any cached blocks. this is necessary to cover the (rare) case
|
||||
// of file cache contents predating the file write.
|
||||
@ -99,19 +99,19 @@ public:
|
||||
const bool isCacheHit = m_fileCache.Retrieve(pathname, fileContents, size);
|
||||
if(!isCacheHit)
|
||||
{
|
||||
VfsDirectory* vfsDirectory; VfsFile* vfsFile;
|
||||
CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, vfsDirectory, &vfsFile));
|
||||
VfsDirectory* directory; VfsFile* file;
|
||||
CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file));
|
||||
|
||||
size = vfsFile->Size();
|
||||
size = file->Size();
|
||||
if(size > ChooseCacheSize())
|
||||
{
|
||||
fileContents = io_Allocate(size);
|
||||
RETURN_ERR(vfsFile->Load(fileContents));
|
||||
RETURN_ERR(file->Load(fileContents));
|
||||
}
|
||||
else
|
||||
{
|
||||
fileContents = m_fileCache.Reserve(size);
|
||||
RETURN_ERR(vfsFile->Load(fileContents));
|
||||
RETURN_ERR(file->Load(fileContents));
|
||||
m_fileCache.Add(pathname, fileContents, size);
|
||||
}
|
||||
}
|
||||
@ -141,9 +141,9 @@ public:
|
||||
|
||||
virtual LibError GetRealPath(const VfsPath& pathname, char* realPathname)
|
||||
{
|
||||
VfsDirectory* vfsDirectory;
|
||||
CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, vfsDirectory, 0));
|
||||
PRealDirectory realDirectory = vfsDirectory->AssociatedDirectory();
|
||||
VfsDirectory* directory;
|
||||
CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0));
|
||||
PRealDirectory realDirectory = directory->AssociatedDirectory();
|
||||
const std::string& name = pathname.leaf();
|
||||
path_append(realPathname, realDirectory->GetPath().string().c_str(), name.c_str());
|
||||
return INFO::OK;
|
||||
|
@ -32,8 +32,8 @@ static size_t s_numArchivedFiles;
|
||||
class PopulateHelper : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
PopulateHelper(VfsDirectory* vfsDirectory, PRealDirectory realDirectory)
|
||||
: m_vfsDirectory(vfsDirectory), m_realDirectory(realDirectory)
|
||||
PopulateHelper(VfsDirectory* directory, PRealDirectory realDirectory)
|
||||
: m_directory(directory), m_realDirectory(realDirectory)
|
||||
{
|
||||
}
|
||||
|
||||
@ -50,8 +50,8 @@ public:
|
||||
private:
|
||||
void AddFile(const FileInfo& fileInfo) const
|
||||
{
|
||||
const VfsFile file(fileInfo, m_realDirectory->Priority(), m_realDirectory);
|
||||
const VfsFile* pfile = m_vfsDirectory->AddFile(file);
|
||||
const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
|
||||
const VfsFile* pfile = m_directory->AddFile(file);
|
||||
|
||||
// notify archive builder that this file could be archived but
|
||||
// currently isn't; if there are too many of these, archive will
|
||||
@ -69,10 +69,10 @@ private:
|
||||
// (we have to create missing subdirectoryNames because archivers
|
||||
// don't always place directory entries before their files)
|
||||
const unsigned flags = VFS_LOOKUP_ADD;
|
||||
VfsDirectory* vfsDirectory;
|
||||
WARN_ERR(vfs_Lookup(pathname, this_->m_vfsDirectory, vfsDirectory, 0, flags));
|
||||
const VfsFile vfsFile(fileInfo, this_->m_realDirectory->Priority(), archiveFile);
|
||||
vfsDirectory->AddFile(vfsFile);
|
||||
VfsDirectory* directory;
|
||||
WARN_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags));
|
||||
const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
|
||||
directory->AddFile(file);
|
||||
s_numArchivedFiles++;
|
||||
}
|
||||
|
||||
@ -106,27 +106,27 @@ private:
|
||||
if(strcasecmp(subdirectoryNames[i].c_str(), ".svn") == 0)
|
||||
continue;
|
||||
|
||||
VfsDirectory* subdirectory = m_vfsDirectory->AddSubdirectory(subdirectoryNames[i]);
|
||||
VfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]);
|
||||
subdirectory->Attach(CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i]));
|
||||
}
|
||||
}
|
||||
|
||||
VfsDirectory* const m_vfsDirectory;
|
||||
VfsDirectory* const m_directory;
|
||||
PRealDirectory m_realDirectory;
|
||||
};
|
||||
|
||||
|
||||
static LibError Populate(VfsDirectory* vfsDirectory)
|
||||
static LibError Populate(VfsDirectory* directory)
|
||||
{
|
||||
if(!vfsDirectory->ShouldPopulate())
|
||||
if(!directory->ShouldPopulate())
|
||||
return INFO::OK;
|
||||
|
||||
PRealDirectory realDirectory = vfsDirectory->AssociatedDirectory();
|
||||
PRealDirectory realDirectory = directory->AssociatedDirectory();
|
||||
|
||||
if(realDirectory->Flags() & VFS_MOUNT_WATCH)
|
||||
realDirectory->Watch();
|
||||
|
||||
PopulateHelper helper(vfsDirectory, realDirectory);
|
||||
PopulateHelper helper(directory, realDirectory);
|
||||
RETURN_ERR(helper.AddEntries());
|
||||
|
||||
return INFO::OK;
|
||||
@ -135,14 +135,14 @@ static LibError Populate(VfsDirectory* vfsDirectory)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* vfsStartDirectory, VfsDirectory*& vfsDirectory, VfsFile** pvfsFile, unsigned flags)
|
||||
LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, unsigned flags)
|
||||
{
|
||||
TIMER_ACCRUE(tc_lookup);
|
||||
|
||||
vfsDirectory = vfsStartDirectory;
|
||||
if(pvfsFile)
|
||||
*pvfsFile = 0;
|
||||
RETURN_ERR(Populate(vfsDirectory));
|
||||
directory = startDirectory;
|
||||
if(pfile)
|
||||
*pfile = 0;
|
||||
RETURN_ERR(Populate(directory));
|
||||
|
||||
if(pathname.empty()) // early out for root directory
|
||||
return INFO::OK; // (prevents iterator error below)
|
||||
@ -155,13 +155,13 @@ TIMER_ACCRUE(tc_lookup);
|
||||
{
|
||||
const std::string& subdirectoryName = *it;
|
||||
|
||||
VfsDirectory* vfsSubdirectory = vfsDirectory->GetSubdirectory(subdirectoryName);
|
||||
if(!vfsSubdirectory)
|
||||
VfsDirectory* subdirectory = directory->GetSubdirectory(subdirectoryName);
|
||||
if(!subdirectory)
|
||||
{
|
||||
if(!(flags & VFS_LOOKUP_ADD))
|
||||
return ERR::VFS_DIR_NOT_FOUND; // NOWARN
|
||||
|
||||
vfsSubdirectory = vfsDirectory->AddSubdirectory(subdirectoryName);
|
||||
subdirectory = directory->AddSubdirectory(subdirectoryName);
|
||||
|
||||
if(flags & VFS_LOOKUP_CREATE)
|
||||
{
|
||||
@ -171,21 +171,21 @@ TIMER_ACCRUE(tc_lookup);
|
||||
(void)mkdir(currentPath.external_directory_string().c_str(), S_IRWXO|S_IRWXU|S_IRWXG);
|
||||
|
||||
PRealDirectory realDirectory(new RealDirectory(currentPath.string(), 0, 0));
|
||||
vfsSubdirectory->Attach(realDirectory);
|
||||
subdirectory->Attach(realDirectory);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
vfsDirectory = vfsSubdirectory;
|
||||
RETURN_ERR(Populate(vfsDirectory));
|
||||
directory = subdirectory;
|
||||
RETURN_ERR(Populate(directory));
|
||||
}
|
||||
|
||||
if(pvfsFile)
|
||||
if(pfile)
|
||||
{
|
||||
const std::string& filename = *it;
|
||||
debug_assert(filename != "."); // asked for file but specified directory path
|
||||
*pvfsFile = vfsDirectory->GetFile(filename);
|
||||
if(!*pvfsFile)
|
||||
*pfile = directory->GetFile(filename);
|
||||
if(!*pfile)
|
||||
return ERR::VFS_FILE_NOT_FOUND; // NOWARN
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,6 @@ enum VfsLookupFlags
|
||||
*
|
||||
* to allow noiseless file-existence queries, this does not raise warnings.
|
||||
**/
|
||||
extern LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* vfsStartDirectory, VfsDirectory*& vfsDirectory, VfsFile** vfsFile, unsigned flags = 0);
|
||||
extern LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, unsigned flags = 0);
|
||||
|
||||
#endif // #ifndef INCLUDED_VFS_LOOKUP
|
||||
|
@ -1,21 +1,7 @@
|
||||
#include "precompiled.h"
|
||||
#include "vfs_path.h"
|
||||
|
||||
bool IsDirectory(const VfsPath& pathname)
|
||||
bool vfs_path_IsDirectory(const VfsPath& pathname)
|
||||
{
|
||||
return pathname.empty() || pathname.leaf() == ".";
|
||||
}
|
||||
|
||||
std::string Basename(const VfsPath& pathname)
|
||||
{
|
||||
const std::string name(pathname.leaf());
|
||||
const size_t pos = name.rfind('.');
|
||||
return name.substr(0, pos);
|
||||
}
|
||||
|
||||
std::string Extension(const VfsPath& pathname)
|
||||
{
|
||||
const std::string name(pathname.leaf());
|
||||
const size_t pos = name.rfind('.');
|
||||
return (pos == std::string::npos)? std::string() : name.substr(pos);
|
||||
}
|
||||
|
@ -22,8 +22,6 @@ struct VfsPathTraits
|
||||
}
|
||||
};
|
||||
|
||||
extern bool IsDirectory(const VfsPath& pathname);
|
||||
extern std::string Basename(const VfsPath& pathname);
|
||||
extern std::string Extension(const VfsPath& pathname);
|
||||
extern bool vfs_path_IsDirectory(const VfsPath& pathname);
|
||||
|
||||
#endif // #ifndef INCLUDED_VFS_PATH
|
||||
|
@ -17,25 +17,25 @@
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
VfsFile::VfsFile(const FileInfo& fileInfo, unsigned priority, PIFileLoader loader)
|
||||
: m_fileInfo(fileInfo), m_priority(priority), m_loader(loader)
|
||||
VfsFile::VfsFile(const std::string& name, off_t size, time_t mtime, unsigned priority, PIFileLoader loader)
|
||||
: m_name(name), m_size(size), m_mtime(mtime), m_priority(priority), m_loader(loader)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool VfsFile::IsSupersededBy(const VfsFile& vfsFile) const
|
||||
bool VfsFile::IsSupersededBy(const VfsFile& file) const
|
||||
{
|
||||
// 1) priority is lower => no.
|
||||
if(vfsFile.m_priority < m_priority)
|
||||
if(file.m_priority < m_priority)
|
||||
return false;
|
||||
|
||||
// 2) timestamp is older => no.
|
||||
// (note: we need to account for FAT's 2 sec. resolution)
|
||||
if(difftime(vfsFile.m_fileInfo.MTime(), m_fileInfo.MTime()) < -2.0)
|
||||
if(difftime(file.MTime(), MTime()) < -2.0)
|
||||
return false;
|
||||
|
||||
// 3) provider is less efficient => no.
|
||||
if(vfsFile.m_loader->Precedence() < m_loader->Precedence())
|
||||
if(file.m_loader->Precedence() < m_loader->Precedence())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -45,19 +45,19 @@ bool VfsFile::IsSupersededBy(const VfsFile& vfsFile) const
|
||||
void VfsFile::GenerateDescription(char* text, size_t maxChars) const
|
||||
{
|
||||
char timestamp[25];
|
||||
const time_t mtime = m_fileInfo.MTime();
|
||||
const time_t mtime = MTime();
|
||||
strftime(timestamp, ARRAY_SIZE(timestamp), "%a %b %d %H:%M:%S %Y", localtime(&mtime));
|
||||
|
||||
// build format string (set width of name field so that everything
|
||||
// lines up correctly)
|
||||
const char* fmt = "(%c; %6d; %s)\n";
|
||||
sprintf_s(text, maxChars, fmt, m_loader->LocationCode(), m_fileInfo.Size(), timestamp);
|
||||
sprintf_s(text, maxChars, fmt, m_loader->LocationCode(), Size(), timestamp);
|
||||
}
|
||||
|
||||
|
||||
LibError VfsFile::Load(shared_ptr<u8> buf) const
|
||||
{
|
||||
return m_loader->Load(m_fileInfo.Name(), buf, m_fileInfo.Size());
|
||||
return m_loader->Load(Name(), buf, Size());
|
||||
}
|
||||
|
||||
|
||||
@ -69,19 +69,19 @@ VfsDirectory::VfsDirectory()
|
||||
}
|
||||
|
||||
|
||||
VfsFile* VfsDirectory::AddFile(const VfsFile& vfsFile)
|
||||
VfsFile* VfsDirectory::AddFile(const VfsFile& file)
|
||||
{
|
||||
std::pair<std::string, VfsFile> value = std::make_pair(vfsFile.Name(), vfsFile);
|
||||
std::pair<VfsFiles::iterator, bool> ret = m_vfsFiles.insert(value);
|
||||
std::pair<std::string, VfsFile> value = std::make_pair(file.Name(), file);
|
||||
std::pair<VfsFiles::iterator, bool> ret = m_files.insert(value);
|
||||
if(!ret.second) // already existed
|
||||
{
|
||||
VfsFile& vfsPreviousFile = ret.first->second;
|
||||
const VfsFile& vfsNewFile = value.second;
|
||||
if(vfsPreviousFile.IsSupersededBy(vfsNewFile))
|
||||
vfsPreviousFile = vfsNewFile;
|
||||
VfsFile& previousFile = ret.first->second;
|
||||
const VfsFile& newFile = value.second;
|
||||
if(previousFile.IsSupersededBy(newFile))
|
||||
previousFile = newFile;
|
||||
}
|
||||
else
|
||||
stats_vfs_file_add(vfsFile.Size());
|
||||
stats_vfs_file_add(file.Size());
|
||||
|
||||
return &(*ret.first).second;
|
||||
}
|
||||
@ -92,15 +92,15 @@ VfsFile* VfsDirectory::AddFile(const VfsFile& vfsFile)
|
||||
VfsDirectory* VfsDirectory::AddSubdirectory(const std::string& name)
|
||||
{
|
||||
std::pair<std::string, VfsDirectory> value = std::make_pair(name, VfsDirectory());
|
||||
std::pair<VfsSubdirectories::iterator, bool> ret = m_vfsSubdirectories.insert(value);
|
||||
std::pair<VfsSubdirectories::iterator, bool> ret = m_subdirectories.insert(value);
|
||||
return &(*ret.first).second;
|
||||
}
|
||||
|
||||
|
||||
VfsFile* VfsDirectory::GetFile(const std::string& name)
|
||||
{
|
||||
VfsFiles::iterator it = m_vfsFiles.find(name);
|
||||
if(it == m_vfsFiles.end())
|
||||
VfsFiles::iterator it = m_files.find(name);
|
||||
if(it == m_files.end())
|
||||
return 0;
|
||||
return &it->second;
|
||||
}
|
||||
@ -108,8 +108,8 @@ VfsFile* VfsDirectory::GetFile(const std::string& name)
|
||||
|
||||
VfsDirectory* VfsDirectory::GetSubdirectory(const std::string& name)
|
||||
{
|
||||
VfsSubdirectories::iterator it = m_vfsSubdirectories.find(name);
|
||||
if(it == m_vfsSubdirectories.end())
|
||||
VfsSubdirectories::iterator it = m_subdirectories.find(name);
|
||||
if(it == m_subdirectories.end())
|
||||
return 0;
|
||||
return &it->second;
|
||||
}
|
||||
@ -119,19 +119,17 @@ void VfsDirectory::GetEntries(FileInfos* files, DirectoryNames* subdirectoryName
|
||||
{
|
||||
if(files)
|
||||
{
|
||||
// (note: VfsFile doesn't return a pointer to FileInfo; instead,
|
||||
// we have it write directly into the files container)
|
||||
files->resize(m_vfsFiles.size());
|
||||
size_t i = 0;
|
||||
for(VfsFiles::const_iterator it = m_vfsFiles.begin(); it != m_vfsFiles.end(); ++it)
|
||||
it->second.GetFileInfo(&(*files)[i++]);
|
||||
files->clear();
|
||||
files->reserve(m_files.size());
|
||||
for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it)
|
||||
files->push_back(FileInfo(it->second.Name(), it->second.Size(), it->second.MTime()));
|
||||
}
|
||||
|
||||
if(subdirectoryNames)
|
||||
{
|
||||
subdirectoryNames->clear();
|
||||
subdirectoryNames->reserve(m_vfsSubdirectories.size());
|
||||
for(VfsSubdirectories::const_iterator it = m_vfsSubdirectories.begin(); it != m_vfsSubdirectories.end(); ++it)
|
||||
subdirectoryNames->reserve(m_subdirectories.size());
|
||||
for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
||||
subdirectoryNames->push_back(it->first);
|
||||
}
|
||||
}
|
||||
@ -145,40 +143,40 @@ void VfsDirectory::DisplayR(unsigned depth) const
|
||||
char fmt[20];
|
||||
sprintf_s(fmt, ARRAY_SIZE(fmt), "%%-%d.%ds %%s", maxNameChars, maxNameChars);
|
||||
|
||||
for(VfsFiles::const_iterator it = m_vfsFiles.begin(); it != m_vfsFiles.end(); ++it)
|
||||
for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it)
|
||||
{
|
||||
const std::string& name = it->first;
|
||||
const VfsFile& vfsFile = it->second;
|
||||
const VfsFile& file = it->second;
|
||||
|
||||
char description[100];
|
||||
vfsFile.GenerateDescription(description, ARRAY_SIZE(description));
|
||||
file.GenerateDescription(description, ARRAY_SIZE(description));
|
||||
|
||||
for(unsigned i = 0; i < depth+1; i++)
|
||||
printf(indent);
|
||||
printf(fmt, name.c_str(), description);
|
||||
}
|
||||
|
||||
for(VfsSubdirectories::const_iterator it = m_vfsSubdirectories.begin(); it != m_vfsSubdirectories.end(); ++it)
|
||||
for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
||||
{
|
||||
const std::string& name = it->first;
|
||||
const VfsDirectory& vfsDirectory = it->second;
|
||||
const VfsDirectory& directory = it->second;
|
||||
|
||||
for(unsigned i = 0; i < depth+1; i++)
|
||||
printf(indent);
|
||||
printf("[%s/]\n", name.c_str());
|
||||
|
||||
vfsDirectory.DisplayR(depth+1);
|
||||
directory.DisplayR(depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VfsDirectory::ClearR()
|
||||
{
|
||||
for(VfsSubdirectories::iterator it = m_vfsSubdirectories.begin(); it != m_vfsSubdirectories.end(); ++it)
|
||||
for(VfsSubdirectories::iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
||||
it->second.ClearR();
|
||||
|
||||
m_vfsFiles.clear();
|
||||
m_vfsSubdirectories.clear();
|
||||
m_files.clear();
|
||||
m_subdirectories.clear();
|
||||
m_realDirectory.reset();
|
||||
m_shouldPopulate = 0;
|
||||
}
|
||||
|
@ -18,31 +18,33 @@
|
||||
class VfsFile
|
||||
{
|
||||
public:
|
||||
VfsFile(const FileInfo& fileInfo, unsigned priority, PIFileLoader provider);
|
||||
VfsFile(const std::string& name, off_t size, time_t mtime, unsigned priority, PIFileLoader provider);
|
||||
|
||||
const std::string& Name() const
|
||||
{
|
||||
return m_fileInfo.Name();
|
||||
return m_name;
|
||||
}
|
||||
|
||||
off_t Size() const
|
||||
{
|
||||
return m_fileInfo.Size();
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void GetFileInfo(FileInfo* pfileInfo) const
|
||||
time_t MTime() const
|
||||
{
|
||||
*pfileInfo = m_fileInfo;
|
||||
return m_mtime;
|
||||
}
|
||||
|
||||
bool IsSupersededBy(const VfsFile& vfsFile) const;
|
||||
bool IsSupersededBy(const VfsFile& file) const;
|
||||
|
||||
void GenerateDescription(char* text, size_t maxChars) const;
|
||||
|
||||
LibError Load(shared_ptr<u8> buf) const;
|
||||
|
||||
private:
|
||||
mutable FileInfo m_fileInfo;
|
||||
std::string m_name;
|
||||
off_t m_size;
|
||||
time_t m_mtime;
|
||||
|
||||
unsigned m_priority;
|
||||
|
||||
@ -59,7 +61,7 @@ public:
|
||||
* @return address of existing or newly inserted file; remains
|
||||
* valid until ClearR is called (i.e. VFS is rebuilt).
|
||||
**/
|
||||
VfsFile* AddFile(const VfsFile& vfsFile);
|
||||
VfsFile* AddFile(const VfsFile& file);
|
||||
|
||||
/**
|
||||
* @return address of existing or newly inserted subdirectory; remains
|
||||
@ -92,10 +94,10 @@ public:
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, VfsFile> VfsFiles;
|
||||
VfsFiles m_vfsFiles;
|
||||
VfsFiles m_files;
|
||||
|
||||
typedef std::map<std::string, VfsDirectory> VfsSubdirectories;
|
||||
VfsSubdirectories m_vfsSubdirectories;
|
||||
VfsSubdirectories m_subdirectories;
|
||||
|
||||
PRealDirectory m_realDirectory;
|
||||
volatile uintptr_t m_shouldPopulate; // (cpu_CAS can't be used on bool)
|
||||
|
@ -1,576 +0,0 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : lf_alloc.cpp
|
||||
* Project : 0 A.D.
|
||||
* Description : lock-free memory allocation.
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
// license: GPL; see lib/license.txt
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#if 0
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
|
||||
#include "posix.h"
|
||||
#include "lib/sysdep/cpu.h"
|
||||
#include "lockfree.h"
|
||||
#include "timer.h"
|
||||
|
||||
|
||||
// superblock descriptor structure
|
||||
// one machine word
|
||||
struct Anchor
|
||||
{
|
||||
uint avail : 10;
|
||||
uint count : 10;
|
||||
uint tag : 10;
|
||||
uint state : 2;
|
||||
|
||||
// convert to uintptr_t for cpu_CAS
|
||||
operator uintptr_t() const
|
||||
{
|
||||
return *(uintptr_t*)this;
|
||||
}
|
||||
};
|
||||
|
||||
cassert(sizeof(Anchor) == sizeof(uintptr_t));
|
||||
|
||||
enum State
|
||||
{
|
||||
ACTIVE = 0,
|
||||
FULL = 1,
|
||||
PARTIAL = 2,
|
||||
EMPTY = 3
|
||||
};
|
||||
|
||||
|
||||
/*typedef void* DescList;
|
||||
|
||||
struct SizeClass
|
||||
{
|
||||
DescList partial; // initially empty
|
||||
size_t sz; // block size
|
||||
size_t sb_size; // superblock's size
|
||||
};
|
||||
|
||||
struct Descriptor;
|
||||
|
||||
static const uint PTR_BITS = sizeof(void*) * CHAR_BIT;
|
||||
|
||||
struct Active
|
||||
{
|
||||
uint pdesc : PTR_BITS-6;
|
||||
uint credits : 6;
|
||||
|
||||
Active()
|
||||
{
|
||||
}
|
||||
|
||||
// convert to uintptr_t for cpu_CAS
|
||||
operator uintptr_t() const
|
||||
{
|
||||
return *(uintptr_t*)this;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// allow Active to be used as Descriptor*
|
||||
//
|
||||
|
||||
Active& operator=(Descriptor* desc)
|
||||
{
|
||||
*(Descriptor**)this = desc;
|
||||
debug_assert(credits == 0); // make sure ptr is aligned
|
||||
return *this;
|
||||
}
|
||||
|
||||
Active(Descriptor* desc)
|
||||
{
|
||||
*this = desc;
|
||||
}
|
||||
|
||||
// disambiguate (could otherwise be either uintptr_t or Descriptor*)
|
||||
bool operator!() const
|
||||
{
|
||||
return (uintptr_t)*this != 0;
|
||||
}
|
||||
|
||||
operator Descriptor*() const
|
||||
{
|
||||
return *(Descriptor**)this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static const uint MAX_CREDITS = 64; // = 2 ** num_credit_bits
|
||||
|
||||
struct ProcHeap
|
||||
{
|
||||
Active active; // initially 0; points to Descriptor
|
||||
Descriptor* partial; // initially 0
|
||||
SizeClass* sc; // parent
|
||||
};
|
||||
|
||||
// POD; must be MAX_CREDITS-aligned!
|
||||
struct Descriptor
|
||||
{
|
||||
Anchor anchor;
|
||||
Descriptor* next;
|
||||
u8* sb; // superblock
|
||||
ProcHeap* heap; // -> owner procheap
|
||||
size_t sz; // block size
|
||||
uint maxcount; // superblock size/sz
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static u8* AllocNewSB(size_t sb_size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void FreeSB(u8* sb)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static Descriptor* DescAvail = 0;
|
||||
|
||||
static const size_t DESCSBSIZE = 128;
|
||||
|
||||
static Descriptor* DescAlloc()
|
||||
{
|
||||
Descriptor* desc;
|
||||
for(;;)
|
||||
{
|
||||
desc = DescAvail;
|
||||
if(desc)
|
||||
{
|
||||
Descriptor* next = desc->next;
|
||||
if(cpu_CAS(&DescAvail, desc, next))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = (Descriptor*)AllocNewSB(DESCSBSIZE);
|
||||
// organize descriptors in a linked list
|
||||
cpu_MemoryFence();
|
||||
if(cpu_CAS(&DescAvail, 0, desc->next))
|
||||
break;
|
||||
FreeSB((u8*)desc);
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
static void DescRetire(Descriptor* desc)
|
||||
{
|
||||
Descriptor* old_head;
|
||||
do
|
||||
{
|
||||
old_head = DescAvail;
|
||||
desc->next = old_head;
|
||||
cpu_MemoryFence();
|
||||
}
|
||||
while(!cpu_CAS(&DescAvail, old_head, desc));
|
||||
}
|
||||
|
||||
static Descriptor* ListGetPartial(SizeClass* sc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ListPutPartial(Descriptor* desc)
|
||||
{
|
||||
}
|
||||
|
||||
static void ListRemoveEmptyDesc(SizeClass* sc)
|
||||
{
|
||||
}
|
||||
|
||||
static ProcHeap* find_heap(SizeClass* sc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static Descriptor* HeapGetPartial(ProcHeap* heap)
|
||||
{
|
||||
Descriptor* desc;
|
||||
do
|
||||
{
|
||||
desc = heap->partial;
|
||||
if(!desc)
|
||||
return ListGetPartial(heap->sc);
|
||||
}
|
||||
while(!cpu_CAS(&heap->partial, desc, 0));
|
||||
return desc;
|
||||
}
|
||||
|
||||
|
||||
static void HeapPutPartial(Descriptor* desc)
|
||||
{
|
||||
Descriptor* prev;
|
||||
do
|
||||
prev = desc->heap->partial;
|
||||
while(!cpu_CAS(&desc->heap->partial, prev, desc));
|
||||
if(prev)
|
||||
ListPutPartial(prev);
|
||||
}
|
||||
|
||||
|
||||
static void UpdateActive(ProcHeap* heap, Descriptor* desc, uint more_credits)
|
||||
{
|
||||
Active new_active = desc;
|
||||
new_active.credits = more_credits-1;
|
||||
if(cpu_CAS(&heap->active, 0, new_active))
|
||||
return;
|
||||
|
||||
// someone installed another active sb
|
||||
// return credits to sb and make it partial
|
||||
Anchor old_anchor, new_anchor;
|
||||
do
|
||||
{
|
||||
new_anchor = old_anchor = desc->anchor;
|
||||
new_anchor.count += more_credits;
|
||||
new_anchor.state = PARTIAL;
|
||||
}
|
||||
while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor));
|
||||
HeapPutPartial(desc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void RemoveEmptyDesc(ProcHeap* heap, Descriptor* desc)
|
||||
{
|
||||
if(cpu_CAS(&heap->partial, desc, 0))
|
||||
DescRetire(desc);
|
||||
else
|
||||
ListRemoveEmptyDesc(heap->sc);
|
||||
}
|
||||
|
||||
|
||||
static void* MallocFromActive(ProcHeap* heap)
|
||||
{
|
||||
// reserve block
|
||||
Active old_active, new_active;
|
||||
do
|
||||
{
|
||||
new_active = old_active = heap->active;
|
||||
// no active superblock - will try Partial and then NewSB
|
||||
if(!old_active)
|
||||
return 0;
|
||||
// none left - mark as no longer active
|
||||
if(old_active.credits == 0)
|
||||
new_active = 0;
|
||||
// expected case - reserve
|
||||
else
|
||||
new_active.credits--;
|
||||
}
|
||||
while(!cpu_CAS(&heap->active, old_active, new_active));
|
||||
|
||||
u8* p;
|
||||
|
||||
// pop block
|
||||
Anchor old_anchor, new_anchor;
|
||||
Descriptor* desc = old_active;
|
||||
uint more_credits;
|
||||
do
|
||||
{
|
||||
new_anchor = old_anchor = desc->anchor;
|
||||
p = desc->sb + old_anchor.avail*desc->sz;
|
||||
new_anchor.avail = *(uint*)p;
|
||||
new_anchor.tag++;
|
||||
if(old_active.credits == 0)
|
||||
{
|
||||
// state must be ACTIVE
|
||||
if(old_anchor.count == 0)
|
||||
new_anchor.state = FULL;
|
||||
else
|
||||
{
|
||||
more_credits = std::min(old_anchor.count, MAX_CREDITS);
|
||||
new_anchor.count -= more_credits;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor));
|
||||
if(old_active.credits == 0 && old_anchor.count > 0)
|
||||
UpdateActive(heap, desc, more_credits);
|
||||
|
||||
*(Descriptor**)p = desc;
|
||||
return p+sizeof(void*);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void* MallocFromPartial(ProcHeap* heap)
|
||||
{
|
||||
retry:
|
||||
Descriptor* desc = HeapGetPartial(heap);
|
||||
if(!desc)
|
||||
return 0;
|
||||
desc->heap = heap;
|
||||
|
||||
// reserve blocks
|
||||
uint more_credits;
|
||||
Anchor old_anchor, new_anchor;
|
||||
do
|
||||
{
|
||||
new_anchor = old_anchor = desc->anchor;
|
||||
if(old_anchor.state == EMPTY)
|
||||
{
|
||||
DescRetire(desc);
|
||||
goto retry;
|
||||
}
|
||||
// old_anchor state must be PARTIAL
|
||||
// old_anchor count must be > 0
|
||||
more_credits = std::min(old_anchor.count-1, MAX_CREDITS);
|
||||
new_anchor.count -= more_credits+1;
|
||||
new_anchor.state = (more_credits > 0)? ACTIVE : FULL;
|
||||
}
|
||||
while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor));
|
||||
|
||||
u8* p;
|
||||
|
||||
// pop reserved block
|
||||
do
|
||||
{
|
||||
new_anchor = old_anchor = desc->anchor;
|
||||
p = desc->sb + old_anchor.avail*desc->sz;
|
||||
new_anchor.avail = *(uint*)p;
|
||||
new_anchor.tag++;
|
||||
}
|
||||
while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor));
|
||||
|
||||
if(more_credits > 0)
|
||||
UpdateActive(heap, desc, more_credits);
|
||||
|
||||
*(Descriptor**)p = desc;
|
||||
return p+sizeof(void*);
|
||||
}
|
||||
|
||||
|
||||
static void* MallocFromNewSB(ProcHeap* heap)
|
||||
{
|
||||
Descriptor* desc = DescAlloc();
|
||||
desc->sb = AllocNewSB(heap->sc->sb_size);
|
||||
|
||||
//organize blocks in a linked list starting with index 0
|
||||
|
||||
desc->heap = heap;
|
||||
desc->anchor.avail = 1;
|
||||
desc->sz = heap->sc->sz;
|
||||
desc->maxcount = (uint)(heap->sc->sb_size/desc->sz);
|
||||
Active new_active = (Active)desc;
|
||||
new_active.credits = std::min(desc->maxcount-1, MAX_CREDITS)-1;
|
||||
desc->anchor.count = (desc->maxcount-1)-(new_active.credits+1);
|
||||
desc->anchor.state = ACTIVE;
|
||||
cpu_MemoryFence();
|
||||
if(!cpu_CAS(&heap->active, 0, new_active))
|
||||
{
|
||||
FreeSB(desc->sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8* p = desc->sb;
|
||||
|
||||
*(Descriptor**)p = desc;
|
||||
return p+sizeof(void*);
|
||||
}
|
||||
|
||||
|
||||
void* lf_malloc(size_t sz)
|
||||
{
|
||||
void* p;
|
||||
|
||||
// use sz and thread id to find heap
|
||||
ProcHeap* heap = find_heap(0); // TODO: pass SizeClass
|
||||
// large block - allocate directly
|
||||
if(!heap)
|
||||
{
|
||||
p = malloc(sz);
|
||||
if(p)
|
||||
*(size_t*)p = sz|1;
|
||||
return p;
|
||||
}
|
||||
|
||||
retry:
|
||||
p = MallocFromActive(heap);
|
||||
if(p)
|
||||
return p;
|
||||
p = MallocFromPartial(heap);
|
||||
if(p)
|
||||
return p;
|
||||
p = MallocFromNewSB(heap);
|
||||
if(p)
|
||||
return p;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
||||
void lf_free(void* p_)
|
||||
{
|
||||
if(!p_)
|
||||
return;
|
||||
u8* p = (u8*)p_;
|
||||
|
||||
// get block header
|
||||
p -= sizeof(void*);
|
||||
uintptr_t hdr = *(uintptr_t*)p;
|
||||
|
||||
// large block - free directly
|
||||
if(hdr & 1)
|
||||
{
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
|
||||
Descriptor* desc = (Descriptor*)hdr;
|
||||
u8* sb = desc->sb;
|
||||
Anchor old_anchor, new_anchor;
|
||||
ProcHeap* heap;
|
||||
do
|
||||
{
|
||||
new_anchor = old_anchor = desc->anchor;
|
||||
*(size_t*)p = old_anchor.avail;
|
||||
new_anchor.avail = (uint)((p-sb) / desc->sz);
|
||||
if(old_anchor.state == FULL)
|
||||
new_anchor.state = PARTIAL;
|
||||
if(old_anchor.count == desc->maxcount-1)
|
||||
{
|
||||
heap = desc->heap;
|
||||
serialize();
|
||||
new_anchor.state = EMPTY;
|
||||
}
|
||||
else
|
||||
new_anchor.count++;
|
||||
cpu_MemoryFence();
|
||||
}
|
||||
while(!cpu_CAS(&desc->anchor, old_anchor, new_anchor));
|
||||
if(new_anchor.state == EMPTY)
|
||||
{
|
||||
FreeSB(sb);
|
||||
RemoveEmptyDesc(heap, desc);
|
||||
}
|
||||
else if(old_anchor.state == FULL)
|
||||
HeapPutPartial(desc);
|
||||
}
|
||||
|
||||
/*
|
||||
static const int MAX_POOLS = 8;
|
||||
|
||||
// split out of pools[] for more efficient lookup
|
||||
static size_t pool_element_sizes[MAX_POOLS];
|
||||
|
||||
struct Pool
|
||||
{
|
||||
u8* bucket_pos;
|
||||
u8* freelist;
|
||||
}
|
||||
pools[MAX_POOLS];
|
||||
|
||||
static const int num_pools = 0;
|
||||
|
||||
|
||||
const size_t BUCKET_SIZE = 8*KiB;
|
||||
|
||||
static u8* bucket_pos;
|
||||
|
||||
|
||||
// return the pool responsible for <size>, or 0 if not yet set up and
|
||||
// there are already too many pools.
|
||||
static Pool* responsible_pool(size_t size)
|
||||
{
|
||||
Pool* pool = pools;
|
||||
for(int i = 0; i < MAX_POOLS; i++, pool++)
|
||||
if(pool->element_size == size)
|
||||
return pool;
|
||||
|
||||
// need to set up a new pool
|
||||
// .. but there are too many
|
||||
debug_assert(0 <= num_pools && num_pools <= MAX_POOLS);
|
||||
if(num_pools >= MAX_POOLS)
|
||||
{
|
||||
debug_assert(0); // increase MAX_POOLS
|
||||
return 0;
|
||||
}
|
||||
|
||||
pool = &pools[num_pools++];
|
||||
pool->element_size = size;
|
||||
return pool;
|
||||
}
|
||||
|
||||
void* sbh_alloc(size_t size)
|
||||
{
|
||||
// when this allocation is freed, there must be enough room for
|
||||
// our freelist pointer. also ensures alignment.
|
||||
size = round_up(size, 8);
|
||||
|
||||
// would overflow a bucket
|
||||
if(size > BUCKET_SIZE-sizeof(u8*))
|
||||
{
|
||||
debug_assert(0); // size doesn't fit in a bucket
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
|
||||
TNode* node_alloc(size_t size)
|
||||
{
|
||||
// would overflow a bucket
|
||||
if(size > BUCKET_SIZE-sizeof(u8*))
|
||||
{
|
||||
debug_assert(0); // size doesn't fit in a bucket
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = round_up(size, 8);
|
||||
// ensure alignment, since size includes a string
|
||||
const uintptr_t addr = (uintptr_t)bucket_pos;
|
||||
const size_t bytes_used = addr % BUCKET_SIZE;
|
||||
// addr = 0 on first call (no bucket yet allocated)
|
||||
// bytes_used == 0 if a node fit exactly into a bucket
|
||||
if(addr == 0 || bytes_used == 0 || bytes_used+size > BUCKET_SIZE)
|
||||
{
|
||||
u8* const prev_bucket = (u8*)addr - bytes_used;
|
||||
u8* bucket = (u8*)mem_alloc(BUCKET_SIZE, BUCKET_SIZE);
|
||||
if(!bucket)
|
||||
return 0;
|
||||
*(u8**)bucket = prev_bucket;
|
||||
bucket_pos = bucket+round_up(sizeof(u8*), 8);
|
||||
}
|
||||
|
||||
TNode* node = (TNode*)bucket_pos;
|
||||
bucket_pos = (u8*)node+size;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
static void node_free_all()
|
||||
{
|
||||
const uintptr_t addr = (uintptr_t)bucket_pos;
|
||||
u8* bucket = bucket_pos - (addr % BUCKET_SIZE);
|
||||
|
||||
// covers bucket_pos == 0 case
|
||||
while(bucket)
|
||||
{
|
||||
u8* prev_bucket = *(u8**)bucket;
|
||||
mem_free(bucket);
|
||||
bucket = prev_bucket;
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif
|
@ -330,119 +330,3 @@ const char* path_extension(const char* fn)
|
||||
const char* ext = dot+1;
|
||||
return ext;
|
||||
}
|
||||
|
||||
|
||||
LibError path_foreach_component(const char* path_org, PathComponentCb cb, uintptr_t cbData)
|
||||
{
|
||||
CHECK_PATH(path_org);
|
||||
|
||||
// copy into (writeable) buffer so we can 'tokenize' path components by
|
||||
// replacing '/' with '\0'.
|
||||
char path[PATH_MAX];
|
||||
strcpy_s(path, ARRAY_SIZE(path), path_org);
|
||||
char* cur_component = path;
|
||||
|
||||
bool is_dir = true; // until we find a component without slash
|
||||
|
||||
// successively navigate to the next component in <path>.
|
||||
for(;;)
|
||||
{
|
||||
// at end of string - done.
|
||||
// (this happens if <path> is empty or ends with slash)
|
||||
if(*cur_component == '\0')
|
||||
break;
|
||||
|
||||
// find end of cur_component
|
||||
char* slash = (char*)strchr(cur_component, '/');
|
||||
// .. try other separator
|
||||
if(!slash)
|
||||
slash = (char*)strchr(cur_component, '\\');
|
||||
|
||||
// decide its type and 0-terminate
|
||||
// .. filename (by definition)
|
||||
if(!slash)
|
||||
is_dir = false;
|
||||
// .. directory
|
||||
else
|
||||
*slash = '\0'; // 0-terminate cur_component
|
||||
|
||||
path_component_validate(cur_component);
|
||||
|
||||
LibError ret = cb(cur_component, is_dir, cbData);
|
||||
// callback wants to abort - return its value.
|
||||
if(ret != INFO::CB_CONTINUE)
|
||||
return ret;
|
||||
|
||||
// filename is by definition the last component. abort now
|
||||
// in case the callback didn't.
|
||||
if(!is_dir)
|
||||
break;
|
||||
|
||||
// advance to next component
|
||||
// .. undo having replaced '/' with '\0' - this means <path> will
|
||||
// store the complete path up to and including cur_component.
|
||||
*slash = '/';
|
||||
cur_component = slash+1;
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// convenience "class" that simplifies successively appending a filename to
|
||||
// its parent directory. this avoids needing to allocate memory and calling
|
||||
// strlen/strcat. used by wdll_ver and dir_next_ent.
|
||||
// we want to maintain C compatibility, so this isn't a C++ class.
|
||||
|
||||
// write the given directory path into our buffer and set end/chars_left
|
||||
// accordingly. <dir> need not but can end with a directory separator.
|
||||
//
|
||||
// note: <dir> and the filename set via path_package_append_file are separated by
|
||||
// '/'. this is to allow use on portable paths; the function otherwise
|
||||
// does not care if paths are relative/portable/absolute.
|
||||
LibError path_package_set_dir(PathPackage* pp, const char* dir)
|
||||
{
|
||||
// -1 allows for trailing '/' that will be added if not
|
||||
// already present.
|
||||
if(strcpy_s(pp->path, ARRAY_SIZE(pp->path)-1, dir) != 0)
|
||||
WARN_RETURN(ERR::PATH_LENGTH);
|
||||
size_t len = strlen(pp->path);
|
||||
// add directory separator if not already present
|
||||
// .. but only check this if dir != "" (=> len-1 is safe)
|
||||
if(len != 0)
|
||||
{
|
||||
char* last_char = pp->path+len-1;
|
||||
if(!path_is_dir_sep(*last_char))
|
||||
{
|
||||
*(last_char+1) = '/';
|
||||
// note: need to 0-terminate because pp.path is uninitialized
|
||||
// and we overwrite strcpy_s's terminator above.
|
||||
*(last_char+2) = '\0';
|
||||
// only bump by 1 - filename must overwrite '\0'.
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
pp->end = pp->path+len;
|
||||
pp->chars_left = ARRAY_SIZE(pp->path)-len;
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
void path_package_copy(PathPackage* pp_dst, const PathPackage* pp_src)
|
||||
{
|
||||
*pp_dst = *pp_src;
|
||||
const ptrdiff_t end_ofs = pp_src->end - pp_src->path;
|
||||
pp_dst->end = pp_dst->path + end_ofs;
|
||||
}
|
||||
|
||||
|
||||
// append the given filename to the directory established by the last
|
||||
// path_package_set_dir on this package. the whole path is accessible at pp->path.
|
||||
LibError path_package_append_file(PathPackage* pp, const char* path)
|
||||
{
|
||||
CHECK_ERR(strcpy_s(pp->end, pp->chars_left, path));
|
||||
return INFO::OK;
|
||||
}
|
||||
|
@ -21,9 +21,6 @@
|
||||
#ifndef INCLUDED_PATH_UTIL
|
||||
#define INCLUDED_PATH_UTIL
|
||||
|
||||
#include "lib/allocators/string_pool.h"
|
||||
|
||||
|
||||
namespace ERR
|
||||
{
|
||||
const LibError PATH_LENGTH = -100300;
|
||||
@ -175,70 +172,4 @@ LIB_API void path_dir_only(const char* path, char* dir);
|
||||
**/
|
||||
LIB_API const char* path_extension(const char* fn);
|
||||
|
||||
|
||||
/**
|
||||
* callback for each component in a path string.
|
||||
*
|
||||
* if path is empty (i.e. ""), this is not called.
|
||||
*
|
||||
* @param component: 0-terminated name of the component (does not
|
||||
* include any trailing slash!)
|
||||
* @param is_dir indicates if it's a directory (i.e. <component> is
|
||||
* followed by a slash in the original path).
|
||||
* rationale: a bool isn't as nice as a flag or enum, but vfs_tree already
|
||||
* has TNodeType and we don't want to expose that or create a new one.
|
||||
* @param cbData: context parameter that was passed to path_foreach_component.
|
||||
* @return LibError; INFO::CB_CONTINUE to continue operation normally;
|
||||
* anything else will cause path_foreach_component to abort immediately and
|
||||
* return that. no need to 'abort' (e.g. return INFO::OK) after a filename is
|
||||
* encountered - that's taken care of automatically.
|
||||
**/
|
||||
typedef LibError (*PathComponentCb)(const char* component, bool is_dir, uintptr_t cbData);
|
||||
|
||||
/**
|
||||
* call <cb> with <cbData> for each component in <path>.
|
||||
*
|
||||
* @return LibError
|
||||
**/
|
||||
LIB_API LibError path_foreach_component(const char* path, PathComponentCb cb, uintptr_t cbData);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* convenience "class" that simplifies successively appending a filename to
|
||||
* its parent directory. this avoids needing to allocate memory and calling
|
||||
* strlen/strcat. used by wdll_ver and dir_next_ent.
|
||||
* we want to maintain C compatibility, so this isn't a C++ class.
|
||||
**/
|
||||
struct PathPackage
|
||||
{
|
||||
char* end;
|
||||
size_t chars_left;
|
||||
char path[PATH_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* write the given directory path into our buffer and set end/chars_left
|
||||
* accordingly. <dir> need not but can end with a directory separator.
|
||||
*
|
||||
* note: <dir> and the filename set via path_package_append_file are separated by
|
||||
* '/'. this is to allow use on portable paths; the function otherwise
|
||||
* does not care if paths are relative/portable/absolute.
|
||||
* @return LibError
|
||||
**/
|
||||
LIB_API LibError path_package_set_dir(PathPackage* pp, const char* dir);
|
||||
|
||||
/**
|
||||
* copy one PathPackage into another (doing so directly is incorrect!)
|
||||
**/
|
||||
LIB_API void path_package_copy(PathPackage* pp_dst, const PathPackage* pp_src);
|
||||
|
||||
/**
|
||||
* append the given filename to the directory established by the last
|
||||
* path_package_set_dir on this package. the whole path is accessible at pp->path.
|
||||
* @return LibError
|
||||
**/
|
||||
LIB_API LibError path_package_append_file(PathPackage* pp, const char* path);
|
||||
|
||||
#endif // #ifndef INCLUDED_PATH_UTIL
|
||||
|
@ -50,7 +50,7 @@ static void* load_empty_sys_cursor()
|
||||
return sys_cursor;
|
||||
}
|
||||
|
||||
static void* load_sys_cursor(const char* filename, int hx, int hy)
|
||||
static void* load_sys_cursor(const VfsPath& pathname, int hx, int hy)
|
||||
{
|
||||
#if !ALLOW_SYS_CURSOR
|
||||
UNUSED2(filename);
|
||||
@ -59,8 +59,12 @@ static void* load_sys_cursor(const char* filename, int hx, int hy)
|
||||
|
||||
return 0;
|
||||
#else
|
||||
shared_ptr<u8> file; size_t fileSize;
|
||||
if(g_VFS->LoadFile(pathname, file, fileSize) < 0)
|
||||
return 0;
|
||||
|
||||
Tex t;
|
||||
if(tex_load(filename, &t) < 0)
|
||||
if(tex_decode(file, fileSize, &t) < 0)
|
||||
return 0;
|
||||
|
||||
{
|
||||
@ -99,9 +103,9 @@ class GLCursor
|
||||
uint hotspotx, hotspoty;
|
||||
|
||||
public:
|
||||
LibError create(const char* filename, uint hotspotx_, uint hotspoty_)
|
||||
LibError create(const VfsPath& pathname, uint hotspotx_, uint hotspoty_)
|
||||
{
|
||||
ht = ogl_tex_load(filename);
|
||||
ht = ogl_tex_load(pathname);
|
||||
RETURN_ERR(ht);
|
||||
|
||||
(void)ogl_tex_get_size(ht, &w, &h, 0);
|
||||
@ -178,29 +182,30 @@ static void Cursor_dtor(Cursor* c)
|
||||
c->gl_cursor.destroy();
|
||||
}
|
||||
|
||||
static LibError Cursor_reload(Cursor* c, const char* name, Handle)
|
||||
static LibError Cursor_reload(Cursor* c, const VfsPath& name, Handle)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
const VfsPath path("art/textures/cursors");
|
||||
const std::string basename = name.string();
|
||||
|
||||
// read pixel offset of the cursor's hotspot [the bit of it that's
|
||||
// drawn at (g_mouse_x,g_mouse_y)] from file.
|
||||
uint hotspotx = 0, hotspoty = 0;
|
||||
{
|
||||
snprintf(filename, ARRAY_SIZE(filename), "art/textures/cursors/%s.txt", name);
|
||||
const VfsPath pathname(path / (basename + ".txt"));
|
||||
shared_ptr<u8> buf; size_t size;
|
||||
RETURN_ERR(g_VFS->LoadFile(filename, buf, size));
|
||||
RETURN_ERR(g_VFS->LoadFile(pathname, buf, size));
|
||||
std::stringstream s(std::string((const char*)buf.get(), size));
|
||||
s >> hotspotx >> hotspoty;
|
||||
}
|
||||
|
||||
// load actual cursor
|
||||
snprintf(filename, ARRAY_SIZE(filename), "art/textures/cursors/%s.dds", name);
|
||||
const VfsPath pathname(path / (basename + ".dds"));
|
||||
// .. try loading as system cursor (2d, hardware accelerated)
|
||||
c->sys_cursor = load_sys_cursor(filename, hotspotx, hotspoty);
|
||||
c->sys_cursor = load_sys_cursor(pathname, hotspotx, hotspoty);
|
||||
// .. fall back to GLCursor (system cursor code is disabled or failed)
|
||||
if(!c->sys_cursor)
|
||||
{
|
||||
LibError err=c->gl_cursor.create(filename, hotspotx, hotspoty);
|
||||
LibError err=c->gl_cursor.create(pathname, hotspotx, hotspoty);
|
||||
|
||||
if (err == INFO::OK)
|
||||
c->gl_sys_cursor = load_empty_sys_cursor();
|
||||
|
@ -90,7 +90,7 @@ static void Ogl_Shader_init(Ogl_Shader* shdr, va_list args)
|
||||
// have absolutely no effect on a program object that contains these shaders
|
||||
// when the program object is already linked.
|
||||
// So, how can we inform the "parent object" (i.e. the program object) of our change?
|
||||
static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle UNUSED(h))
|
||||
static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const VfsPath& pathname, Handle UNUSED(h))
|
||||
{
|
||||
LibError err = ERR::FAIL;
|
||||
|
||||
@ -98,7 +98,7 @@ static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle
|
||||
return INFO::OK;
|
||||
|
||||
shared_ptr<u8> file; size_t file_size;
|
||||
RETURN_ERR(g_VFS->LoadFile(filename, file, file_size));
|
||||
RETURN_ERR(g_VFS->LoadFile(pathname, file, file_size));
|
||||
|
||||
ogl_WarnIfError();
|
||||
|
||||
@ -131,7 +131,7 @@ static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle
|
||||
pglGetInfoLogARB(shdr->id, log_length, 0, infolog);
|
||||
|
||||
debug_printf("Compile log for shader %hs (type %hs):\n%hs",
|
||||
filename,
|
||||
pathname.string().c_str(),
|
||||
shader_type_to_string(shdr->type, typenamebuf, ARRAY_SIZE(typenamebuf)),
|
||||
infolog);
|
||||
|
||||
@ -148,7 +148,7 @@ static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const char* filename, Handle
|
||||
|
||||
char typenamebuf[32];
|
||||
debug_printf("Failed to compile shader %hs (type %hs)\n",
|
||||
filename,
|
||||
pathname.string().c_str(),
|
||||
shader_type_to_string(shdr->type, typenamebuf, ARRAY_SIZE(typenamebuf)));
|
||||
|
||||
err = ERR::SHDR_COMPILE;
|
||||
@ -194,9 +194,9 @@ static LibError Ogl_Shader_to_string(const Ogl_Shader* UNUSED(shdr), char* buf)
|
||||
// Create, load and compile a shader object of the given type
|
||||
// (e.g. GL_VERTEX_SHADER_ARB). The given file will be used as
|
||||
// source code for the shader.
|
||||
Handle ogl_shader_load(const char* fn, GLenum type)
|
||||
Handle ogl_shader_load(const VfsPath& pathname, GLenum type)
|
||||
{
|
||||
return h_alloc(H_Ogl_Shader, fn, 0, type);
|
||||
return h_alloc(H_Ogl_Shader, pathname, 0, type);
|
||||
}
|
||||
|
||||
|
||||
@ -244,7 +244,7 @@ static void Ogl_Program_init(Ogl_Program* UNUSED(p), va_list UNUSED(args))
|
||||
// Load the shader associated with one Shader element,
|
||||
// and attach it to our program object.
|
||||
static LibError do_load_shader(
|
||||
Ogl_Program* p, const char* filename, Handle UNUSED(h),
|
||||
Ogl_Program* p, const VfsPath& pathname, Handle UNUSED(h),
|
||||
const CXeromyces& XeroFile, const XMBElement& Shader)
|
||||
{
|
||||
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
|
||||
@ -255,8 +255,7 @@ static LibError do_load_shader(
|
||||
|
||||
if (Type.empty())
|
||||
{
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: Missing attribute \"type\" in element \"Shader\".",
|
||||
filename);
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: Missing attribute \"type\" in element \"Shader\".", pathname);
|
||||
WARN_RETURN(ERR::CORRUPTED);
|
||||
}
|
||||
|
||||
@ -264,8 +263,7 @@ static LibError do_load_shader(
|
||||
|
||||
if (!shadertype)
|
||||
{
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: Unknown shader type \"%hs\" (valid are: VERTEX_SHADER, FRAGMENT_SHADER).",
|
||||
filename, Type.c_str());
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: Unknown shader type \"%hs\" (valid are: VERTEX_SHADER, FRAGMENT_SHADER).", pathname, Type.c_str());
|
||||
WARN_RETURN(ERR::CORRUPTED);
|
||||
}
|
||||
|
||||
@ -273,11 +271,11 @@ static LibError do_load_shader(
|
||||
|
||||
if (Name.empty())
|
||||
{
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: Missing shader name.", filename);
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: Missing shader name.", pathname);
|
||||
WARN_RETURN(ERR::CORRUPTED);
|
||||
}
|
||||
|
||||
Handle hshader = ogl_shader_load(Name.c_str(), shadertype);
|
||||
Handle hshader = ogl_shader_load(Name, shadertype);
|
||||
RETURN_ERR(hshader);
|
||||
|
||||
ogl_shader_attach(p->id, hshader);
|
||||
@ -293,7 +291,7 @@ static LibError do_load_shader(
|
||||
|
||||
|
||||
// Reload the program object from the source file.
|
||||
static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle h)
|
||||
static LibError Ogl_Program_reload(Ogl_Program* p, const VfsPath& pathname_, Handle h)
|
||||
{
|
||||
if (p->id)
|
||||
return INFO::OK;
|
||||
@ -310,8 +308,9 @@ static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle
|
||||
WARN_RETURN(ERR::SHDR_CREATE);
|
||||
}
|
||||
|
||||
const char* pathname = pathname_.string().c_str();
|
||||
CXeromyces XeroFile;
|
||||
if (XeroFile.Load(filename) != PSRETURN_OK)
|
||||
if (XeroFile.Load(pathname) != PSRETURN_OK)
|
||||
WARN_RETURN(ERR::CORRUPTED); // more informative error message?
|
||||
|
||||
// Define all the elements and attributes used in the XML file
|
||||
@ -325,7 +324,7 @@ static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle
|
||||
|
||||
if (Root.GetNodeName() != el_program)
|
||||
{
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: XML root was not \"Program\".", filename);
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: XML root was not \"Program\".", pathname);
|
||||
WARN_RETURN(ERR::CORRUPTED);
|
||||
}
|
||||
|
||||
@ -346,17 +345,16 @@ static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle
|
||||
|
||||
if (Shader.GetNodeName() != el_shader)
|
||||
{
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: Only \"Shader\" may be child of \"Shaders\".",
|
||||
filename);
|
||||
LOG(ERROR, LOG_CATEGORY, "%hs: Only \"Shader\" may be child of \"Shaders\".", pathname);
|
||||
WARN_RETURN(ERR::CORRUPTED);
|
||||
}
|
||||
|
||||
RETURN_ERR(do_load_shader(p, filename, h, XeroFile, Shader));
|
||||
RETURN_ERR(do_load_shader(p, pathname, h, XeroFile, Shader));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(WARNING, LOG_CATEGORY, "%hs: Unknown child of \"Program\".", filename);
|
||||
LOG(WARNING, LOG_CATEGORY, "%hs: Unknown child of \"Program\".", pathname);
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,13 +370,13 @@ static LibError Ogl_Program_reload(Ogl_Program* p, const char* filename, Handle
|
||||
char* infolog = new char[log_length];
|
||||
pglGetInfoLogARB(p->id, log_length, 0, infolog);
|
||||
|
||||
debug_printf("Linker log for %hs:\n%hs\n", filename, infolog);
|
||||
debug_printf("Linker log for %hs:\n%hs\n", pathname, infolog);
|
||||
delete[] infolog;
|
||||
}
|
||||
|
||||
if (!linked)
|
||||
{
|
||||
debug_printf("Link failed for %hs\n", filename);
|
||||
debug_printf("Link failed for %hs\n", pathname);
|
||||
WARN_RETURN(ERR::SHDR_LINK);
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ extensions are available, or all bets are off.
|
||||
// Create, load and compile a shader object of the given type
|
||||
// (e.g. GL_VERTEX_SHADER_ARB). The given file will be used as
|
||||
// source code for the shader.
|
||||
Handle ogl_shader_load(const char* fn, GLenum type);
|
||||
Handle ogl_shader_load(const VfsPath& pathname, GLenum type);
|
||||
|
||||
// Free all resources associated with the given handle (subject
|
||||
// to refcounting).
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "../h_mgr.h"
|
||||
#include "lib/file/vfs/vfs.h"
|
||||
#include "lib/fnv_hash.h"
|
||||
extern PIVFS g_VFS;
|
||||
|
||||
|
||||
@ -396,7 +397,7 @@ static void OglTex_dtor(OglTex* ot)
|
||||
ot->flags &= ~OT_IS_UPLOADED;
|
||||
}
|
||||
|
||||
static LibError OglTex_reload(OglTex* ot, const char* fn, Handle h)
|
||||
static LibError OglTex_reload(OglTex* ot, const VfsPath& pathname, Handle h)
|
||||
{
|
||||
// we're reusing a freed but still in-memory OglTex object
|
||||
if(ot->flags & OT_IS_UPLOADED)
|
||||
@ -405,7 +406,12 @@ static LibError OglTex_reload(OglTex* ot, const char* fn, Handle h)
|
||||
// if we don't already have the texture in memory (*), load from file.
|
||||
// * this happens if the texture is "wrapped".
|
||||
if(!(ot->flags & OT_TEX_VALID))
|
||||
RETURN_ERR(tex_load(fn, &ot->t));
|
||||
{
|
||||
shared_ptr<u8> file; size_t fileSize;
|
||||
RETURN_ERR(g_VFS->LoadFile(pathname, file, fileSize));
|
||||
if(tex_decode(file, fileSize, &ot->t) < 0)
|
||||
return 0;
|
||||
}
|
||||
ot->flags |= OT_TEX_VALID;
|
||||
|
||||
glGenTextures(1, &ot->id);
|
||||
@ -467,20 +473,21 @@ static LibError OglTex_to_string(const OglTex* ot, char* buf)
|
||||
}
|
||||
|
||||
|
||||
// load and return a handle to the texture given in <fn>.
|
||||
// load and return a handle to the texture given in <pathname>.
|
||||
// for a list of supported formats, see tex.h's tex_load.
|
||||
Handle ogl_tex_load(const char* fn, uint flags)
|
||||
Handle ogl_tex_load(const VfsPath& pathname, uint flags)
|
||||
{
|
||||
Tex* wrapped_tex = 0; // we're loading from file
|
||||
return h_alloc(H_OglTex, fn, flags, wrapped_tex);
|
||||
return h_alloc(H_OglTex, pathname, flags, wrapped_tex);
|
||||
}
|
||||
|
||||
|
||||
// return Handle to an existing object, if it has been loaded and
|
||||
// is still in memory; otherwise, a negative error code.
|
||||
Handle ogl_tex_find(const char* fn)
|
||||
Handle ogl_tex_find(const VfsPath& pathname)
|
||||
{
|
||||
return h_find(H_OglTex, (uintptr_t)fn);
|
||||
const uintptr_t key = fnv_hash(pathname.string().c_str(), pathname.string().length());
|
||||
return h_find(H_OglTex, key);
|
||||
}
|
||||
|
||||
|
||||
@ -757,16 +764,14 @@ struct UploadParams
|
||||
GLint int_fmt;
|
||||
};
|
||||
|
||||
static void upload_level(uint level, uint level_w, uint level_h,
|
||||
const u8* RESTRICT level_data, size_t UNUSED(level_data_size), void* RESTRICT cbData)
|
||||
static void upload_level(uint level, uint level_w, uint level_h, const u8* RESTRICT level_data, size_t UNUSED(level_data_size), void* RESTRICT cbData)
|
||||
{
|
||||
const UploadParams* up = (const UploadParams*)cbData;
|
||||
glTexImage2D(GL_TEXTURE_2D, level, up->int_fmt, level_w, level_h, 0,
|
||||
up->fmt, GL_UNSIGNED_BYTE, level_data);
|
||||
}
|
||||
|
||||
static void upload_compressed_level(uint level, uint level_w, uint level_h,
|
||||
const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData)
|
||||
static void upload_compressed_level(uint level, uint level_w, uint level_h, const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData)
|
||||
{
|
||||
const UploadParams* up = (const UploadParams*)cbData;
|
||||
pglCompressedTexImage2DARB(GL_TEXTURE_2D, level, up->fmt,
|
||||
@ -807,9 +812,6 @@ LibError ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint
|
||||
|
||||
H_DEREF(ht, OglTex, ot);
|
||||
Tex* t = &ot->t;
|
||||
const char* fn = h_filename(ht);
|
||||
if(!fn)
|
||||
fn = "(could not determine filename)";
|
||||
debug_assert(q_flags_valid(q_flags_ovr));
|
||||
// we don't bother verifying *fmt_ovr - there are too many values
|
||||
|
||||
|
@ -199,12 +199,11 @@ extern void ogl_tex_set_defaults(uint q_flags, GLint filter);
|
||||
/**
|
||||
* Load and return a handle to the texture.
|
||||
*
|
||||
* @param fn VFS filename of texture.
|
||||
* @param flags h_alloc flags.
|
||||
* @return Handle to texture or negative LibError
|
||||
* for a list of supported formats, see tex.h's tex_load.
|
||||
*/
|
||||
extern Handle ogl_tex_load(const char* fn, uint flags = 0);
|
||||
extern Handle ogl_tex_load(const VfsPath& pathname, uint flags = 0);
|
||||
|
||||
/**
|
||||
* Find and return an existing texture object, if it has already been
|
||||
@ -213,7 +212,7 @@ extern Handle ogl_tex_load(const char* fn, uint flags = 0);
|
||||
* @param fn VFS filename of texture.
|
||||
* @return Handle to texture or negative LibError
|
||||
*/
|
||||
extern Handle ogl_tex_find(const char* fn);
|
||||
extern Handle ogl_tex_find(const VfsPath& pathname);
|
||||
|
||||
/**
|
||||
* Make the Tex object ready for use as an OpenGL texture
|
||||
|
@ -6,8 +6,7 @@
|
||||
|
||||
class TestTex : public CxxTest::TestSuite
|
||||
{
|
||||
void generate_encode_decode_compare(uint w, uint h, uint flags, uint bpp,
|
||||
const char* filename)
|
||||
void generate_encode_decode_compare(uint w, uint h, uint flags, uint bpp, const std::string& extension)
|
||||
{
|
||||
// generate test data
|
||||
const size_t size = w*h*bpp/8;
|
||||
@ -21,12 +20,12 @@ class TestTex : public CxxTest::TestSuite
|
||||
|
||||
// encode to file format
|
||||
DynArray da;
|
||||
TS_ASSERT_OK(tex_encode(&t, filename, &da));
|
||||
TS_ASSERT_OK(tex_encode(&t, extension, &da));
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
// decode from file format
|
||||
MEM_DTOR dtor = 0; // we'll free da manually
|
||||
TS_ASSERT_OK(tex_decode(da.base, da.cur_size, dtor, &t));
|
||||
shared_ptr<u8> ptr(&da.base, DummyDeleter());
|
||||
TS_ASSERT_OK(tex_decode(ptr, da.cur_size, 0, &t));
|
||||
|
||||
// make sure pixel format gets converted completely to plain
|
||||
TS_ASSERT_OK(tex_transform_to(&t, 0));
|
||||
|
@ -58,8 +58,9 @@ static void UniFont_dtor(UniFont* f)
|
||||
SAFE_DELETE(f->glyphs_size);
|
||||
}
|
||||
|
||||
// basename is e.g. "console"; the files are "fonts/console.fnt" and "fonts/console.tga"
|
||||
// [10..70ms]
|
||||
static LibError UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h))
|
||||
static LibError UniFont_reload(UniFont* f, const VfsPath& basename, Handle UNUSED(h))
|
||||
{
|
||||
// already loaded
|
||||
if(f->ht > 0)
|
||||
@ -68,16 +69,13 @@ static LibError UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h))
|
||||
f->glyphs_id = new glyphmap_id();
|
||||
f->glyphs_size = new glyphmap_size();
|
||||
|
||||
// fn is the base filename, e.g. "console"
|
||||
// The font definition file is "fonts/"+fn+".fnt" and the texture is "fonts/"+fn+".tga"
|
||||
std::string FilenameBase = "fonts/"; FilenameBase += fn;
|
||||
const VfsPath path("fonts/");
|
||||
|
||||
// Read font definition file into a stringstream
|
||||
const std::string FilenameFnt = FilenameBase+".fnt";
|
||||
const char* fnt_fn = FilenameFnt.c_str();
|
||||
shared_ptr<u8> buf; size_t size;
|
||||
RETURN_ERR(g_VFS->LoadFile(fnt_fn, buf, size)); // [cumulative for 12: 36ms]
|
||||
std::istringstream FNTStream (std::string((const char*)buf.get(), (int)size));
|
||||
const VfsPath fntName(basename.string() + ".fnt");
|
||||
RETURN_ERR(g_VFS->LoadFile(path/fntName, buf, size)); // [cumulative for 12: 36ms]
|
||||
std::istringstream FNTStream (std::string((const char*)buf.get(), size));
|
||||
|
||||
int Version;
|
||||
FNTStream >> Version;
|
||||
@ -145,9 +143,8 @@ static LibError UniFont_reload(UniFont* f, const char* fn, Handle UNUSED(h))
|
||||
|
||||
// Load glyph texture
|
||||
// [cumulative for 12: 20ms]
|
||||
std::string FilenameTex = FilenameBase+".tga";
|
||||
const char* tex_fn = FilenameTex.c_str();
|
||||
Handle ht = ogl_tex_load(tex_fn);
|
||||
const VfsPath tgaName(basename.string() + ".tga");
|
||||
Handle ht = ogl_tex_load(path/tgaName);
|
||||
RETURN_ERR(ht);
|
||||
(void)ogl_tex_set_filter(ht, GL_NEAREST);
|
||||
// override is necessary because the GL format is chosen as LUMINANCE,
|
||||
@ -191,9 +188,9 @@ static LibError UniFont_to_string(const UniFont* f, char* buf)
|
||||
}
|
||||
|
||||
|
||||
Handle unifont_load(const char* fn, uint flags)
|
||||
Handle unifont_load(const VfsPath& pathname, uint flags)
|
||||
{
|
||||
return h_alloc(H_UniFont, fn, flags);
|
||||
return h_alloc(H_UniFont, pathname, flags);
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
// Load and return a handle to the font defined
|
||||
// in fn+".fnt" with texture fn+".tga"
|
||||
extern Handle unifont_load(const char* fn, uint flags = 0);
|
||||
extern Handle unifont_load(const VfsPath& pathname, uint flags = 0);
|
||||
|
||||
// Release a handle to a previously loaded font
|
||||
extern LibError unifont_unload(Handle& h);
|
||||
|
@ -139,7 +139,7 @@ struct HDATA
|
||||
// for statistics
|
||||
uint num_derefs;
|
||||
|
||||
const char* fn;
|
||||
VfsPath pathname;
|
||||
|
||||
u8 user[HDATA_USER_SIZE];
|
||||
};
|
||||
@ -390,79 +390,10 @@ static void key_remove(uintptr_t key, H_Type type)
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// path string suballocator (they're stored in HDATA)
|
||||
//
|
||||
|
||||
static Pool fn_pool;
|
||||
|
||||
// if fn is longer than this, it has to be allocated from the heap.
|
||||
// choose this to balance internal fragmentation and accessing the heap.
|
||||
static const size_t FN_POOL_EL_SIZE = 64;
|
||||
|
||||
static void fn_init()
|
||||
{
|
||||
// (if this fails, so will subsequent fn_stores - no need to complain here)
|
||||
(void)pool_create(&fn_pool, MAX_EXTANT_HANDLES*FN_POOL_EL_SIZE, FN_POOL_EL_SIZE);
|
||||
}
|
||||
|
||||
static void fn_store(HDATA* hd, const char* fn)
|
||||
{
|
||||
const size_t size = strlen(fn)+1;
|
||||
|
||||
hd->fn = 0;
|
||||
// stuff it in unused space at the end of HDATA
|
||||
if(hd->type->user_size+size <= HDATA_USER_SIZE)
|
||||
hd->fn = (const char*)hd->user + hd->type->user_size;
|
||||
else if(size <= FN_POOL_EL_SIZE)
|
||||
hd->fn = (const char*)pool_alloc(&fn_pool, 0);
|
||||
|
||||
// in case none of the above applied and/or were successful:
|
||||
// fall back to heap alloc.
|
||||
if(!hd->fn)
|
||||
{
|
||||
debug_printf("H_MGR| very long filename (%d) %s\n", size, fn);
|
||||
hd->fn = (const char*)malloc(size);
|
||||
// still failed - bail (avoid strcpy to 0)
|
||||
if(!hd->fn)
|
||||
WARN_ERR_RETURN(ERR::NO_MEM);
|
||||
}
|
||||
|
||||
cpu_memcpy((void*)hd->fn, fn, size); // faster than strcpy
|
||||
}
|
||||
|
||||
// TODO: store this in a flag - faster.
|
||||
static bool fn_is_in_HDATA(HDATA* hd)
|
||||
{
|
||||
u8* p = (u8*)hd->fn; // needed for type-correct comparison
|
||||
return (hd->user+hd->type->user_size <= p && p <= hd->user+HDATA_USER_SIZE);
|
||||
}
|
||||
|
||||
static void fn_free(HDATA* hd)
|
||||
{
|
||||
void* el = (void*)hd->fn;
|
||||
if(fn_is_in_HDATA(hd))
|
||||
{
|
||||
}
|
||||
else if(pool_contains(&fn_pool, el))
|
||||
pool_free(&fn_pool, el);
|
||||
else
|
||||
free(el);
|
||||
}
|
||||
|
||||
static void fn_shutdown()
|
||||
{
|
||||
pool_destroy(&fn_pool);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// h_alloc
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
static void warn_if_invalid(HDATA* hd)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
@ -478,13 +409,10 @@ static void warn_if_invalid(HDATA* hd)
|
||||
|
||||
// make sure empty space in control block isn't touched
|
||||
// .. but only if we're not storing a filename there
|
||||
if(!fn_is_in_HDATA(hd))
|
||||
{
|
||||
const u8* start = hd->user + vtbl->user_size;
|
||||
const u8* end = hd->user + HDATA_USER_SIZE;
|
||||
for(const u8* p = start; p < end; p++)
|
||||
debug_assert(*p == 0); // else: handle user data was overrun!
|
||||
}
|
||||
const u8* start = hd->user + vtbl->user_size;
|
||||
const u8* end = hd->user + HDATA_USER_SIZE;
|
||||
for(const u8* p = start; p < end; p++)
|
||||
debug_assert(*p == 0); // else: handle user data was overrun!
|
||||
#else
|
||||
UNUSED2(hd);
|
||||
#endif
|
||||
@ -550,7 +478,7 @@ static Handle reuse_existing_handle(uintptr_t key, H_Type type, uint flags)
|
||||
}
|
||||
|
||||
|
||||
static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const char* fn, va_list* init_args)
|
||||
static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const VfsPath& pathname, va_list* init_args)
|
||||
{
|
||||
LibError err = INFO::OK;
|
||||
H_VTbl* vtbl = type; // exact same thing but for clarity
|
||||
@ -565,7 +493,7 @@ static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const cha
|
||||
// catch exception to simplify reload funcs - let them use new()
|
||||
try
|
||||
{
|
||||
err = vtbl->reload(hd->user, fn, h);
|
||||
err = vtbl->reload(hd->user, pathname, h);
|
||||
if(err == INFO::OK)
|
||||
warn_if_invalid(hd);
|
||||
}
|
||||
@ -579,8 +507,7 @@ static LibError call_init_and_reload(Handle h, H_Type type, HDATA* hd, const cha
|
||||
}
|
||||
|
||||
|
||||
static Handle alloc_new_handle(H_Type type, const char* fn, uintptr_t key,
|
||||
uint flags, va_list* init_args)
|
||||
static Handle alloc_new_handle(H_Type type, const VfsPath& pathname, uintptr_t key, uint flags, va_list* init_args)
|
||||
{
|
||||
i32 idx;
|
||||
HDATA* hd;
|
||||
@ -600,16 +527,12 @@ static Handle alloc_new_handle(H_Type type, const char* fn, uintptr_t key,
|
||||
if(flags & RES_DISALLOW_RELOAD)
|
||||
hd->disallow_reload = 1;
|
||||
hd->unique = (flags & RES_UNIQUE) != 0;
|
||||
hd->fn = 0;
|
||||
// .. filename is valid - store in hd
|
||||
// note: if the original fn param was a key, it was reset to 0 above.
|
||||
if(fn)
|
||||
fn_store(hd, fn);
|
||||
hd->pathname = pathname;
|
||||
|
||||
if(key && !hd->unique)
|
||||
key_add(key, h);
|
||||
|
||||
LibError err = call_init_and_reload(h, type, hd, fn, init_args);
|
||||
LibError err = call_init_and_reload(h, type, hd, pathname, init_args);
|
||||
if(err < 0)
|
||||
goto fail;
|
||||
|
||||
@ -627,30 +550,14 @@ fail:
|
||||
|
||||
|
||||
// any further params are passed to type's init routine
|
||||
Handle h_alloc(H_Type type, const char* fn, uint flags, ...)
|
||||
Handle h_alloc(H_Type type, const VfsPath& pathname, uint flags, ...)
|
||||
{
|
||||
RETURN_ERR(type_validate(type));
|
||||
|
||||
// get key (either hash of filename, or fn param)
|
||||
uintptr_t key = 0;
|
||||
// not backed by file; fn is the key
|
||||
if(flags & RES_KEY)
|
||||
{
|
||||
key = (uintptr_t)fn;
|
||||
fn = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(fn)
|
||||
key = fnv_hash(fn);
|
||||
}
|
||||
const uintptr_t key = fnv_hash(pathname.string().c_str(), pathname.string().length());
|
||||
|
||||
//debug_printf("alloc %s %s\n", type->name, fn);
|
||||
|
||||
// no key => can never be found. disallow caching
|
||||
if(!key)
|
||||
flags |= RES_NO_CACHE;
|
||||
|
||||
// see if we can reuse an existing handle
|
||||
Handle h = reuse_existing_handle(key, type, flags);
|
||||
RETURN_ERR(h);
|
||||
@ -660,7 +567,7 @@ Handle h_alloc(H_Type type, const char* fn, uint flags, ...)
|
||||
// .. need to allocate a new one:
|
||||
va_list args;
|
||||
va_start(args, flags);
|
||||
h = alloc_new_handle(type, fn, key, flags, &args);
|
||||
h = alloc_new_handle(type, pathname, key, flags, &args);
|
||||
va_end(args);
|
||||
return h; // alloc_new_handle already does CHECK_ERR
|
||||
}
|
||||
@ -694,16 +601,6 @@ static LibError h_free_idx(i32 idx, HDATA* hd)
|
||||
if(hd->key && !hd->unique)
|
||||
key_remove(hd->key, hd->type);
|
||||
|
||||
// get pretty version of filename: start with "not applicable"
|
||||
const char* fn = "(0)";
|
||||
if(hd->fn)
|
||||
{
|
||||
// if hd->fn is a filename, strip the path. note: some paths end
|
||||
// with '/', so display those unaltered.
|
||||
const char* slash = strrchr(hd->fn, '/');
|
||||
fn = (slash && slash[1] != '\0')? slash+1 : hd->fn;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// to_string is slow for some handles, so avoid calling it if unnecessary
|
||||
if(debug_filter_allows("H_MGR|"))
|
||||
@ -711,11 +608,11 @@ static LibError h_free_idx(i32 idx, HDATA* hd)
|
||||
char buf[H_STRING_LEN];
|
||||
if(vtbl->to_string(hd->user, buf) < 0)
|
||||
strcpy(buf, "(error)"); // safe
|
||||
debug_printf("H_MGR| free %s %s accesses=%d %s\n", hd->type->name, fn, hd->num_derefs, buf);
|
||||
debug_printf("H_MGR| free %s %s accesses=%d %s\n", hd->type->name, hd->pathname.string().c_str(), hd->num_derefs, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
fn_free(hd);
|
||||
hd->pathname.~VfsPath(); // FIXME: ugly hack, but necessary to reclaim std::string memory
|
||||
|
||||
memset(hd, 0, sizeof(*hd));
|
||||
|
||||
@ -771,7 +668,7 @@ void* h_user_data(const Handle h, const H_Type type)
|
||||
}
|
||||
|
||||
|
||||
const char* h_filename(const Handle h)
|
||||
VfsPath h_filename(const Handle h)
|
||||
{
|
||||
// don't require type check: should be useable for any handle,
|
||||
// even if the caller doesn't know its type.
|
||||
@ -781,19 +678,14 @@ const char* h_filename(const Handle h)
|
||||
debug_assert(0);
|
||||
return 0;
|
||||
}
|
||||
return hd->fn;
|
||||
return hd->pathname;
|
||||
}
|
||||
|
||||
|
||||
// TODO: what if iterating through all handles is too slow?
|
||||
LibError h_reload(const char* fn)
|
||||
LibError h_reload(const VfsPath& pathname)
|
||||
{
|
||||
// must not continue - some resources not backed by files have
|
||||
// key = 0 and reloading those would be disastrous.
|
||||
if(!fn)
|
||||
WARN_RETURN(ERR::INVALID_PARAM);
|
||||
|
||||
const u32 key = fnv_hash(fn);
|
||||
const u32 key = fnv_hash(pathname.string().c_str(), pathname.string().length());
|
||||
|
||||
// destroy (note: not free!) all handles backed by this file.
|
||||
// do this before reloading any of them, because we don't specify reload
|
||||
@ -818,7 +710,7 @@ LibError h_reload(const char* fn)
|
||||
|
||||
Handle h = handle(i, hd->tag);
|
||||
|
||||
LibError err = hd->type->reload(hd->user, hd->fn, h);
|
||||
LibError err = hd->type->reload(hd->user, hd->pathname, h);
|
||||
// don't stop if an error is encountered - try to reload them all.
|
||||
if(err < 0)
|
||||
{
|
||||
@ -901,10 +793,6 @@ static ModuleInitState initState;
|
||||
|
||||
void h_mgr_init()
|
||||
{
|
||||
if(!ModuleShouldInitialize(&initState))
|
||||
return;
|
||||
|
||||
fn_init();
|
||||
}
|
||||
|
||||
|
||||
@ -945,6 +833,4 @@ void h_mgr_shutdown()
|
||||
free(pages[j]);
|
||||
pages[j] = 0;
|
||||
}
|
||||
|
||||
fn_shutdown();
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ reload:
|
||||
does all initialization of the resource that requires its source file.
|
||||
called after init; also after dtor every time the file is reloaded.
|
||||
|
||||
static LibError Type_reload(Res1* r, const char* filename, Handle);
|
||||
static LibError Type_reload(Res1* r, const VfsPath& pathname, Handle);
|
||||
{
|
||||
// already loaded; done
|
||||
if(r->data)
|
||||
@ -126,7 +126,7 @@ static LibError Type_reload(Res1* r, const char* filename, Handle);
|
||||
r->data = malloc(100);
|
||||
if(!r->data)
|
||||
WARN_RETURN(ERR::NO_MEM);
|
||||
// (read contents of <filename> into r->data)
|
||||
// (read contents of <pathname> into r->data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -187,10 +187,10 @@ static LibError Type_validate(const Res1* r);
|
||||
|
||||
|
||||
5) provide your layer on top of the handle manager:
|
||||
Handle res1_load(const char* filename, int my_flags)
|
||||
Handle res1_load(const VfsPath& pathname, int my_flags)
|
||||
{
|
||||
// passes my_flags to init
|
||||
return h_alloc(H_Res1, filename, 0, my_flags);
|
||||
return h_alloc(H_Res1, pathname, 0, my_flags);
|
||||
}
|
||||
|
||||
LibError res1_free(Handle& h)
|
||||
@ -242,33 +242,10 @@ extern void h_mgr_shutdown();
|
||||
|
||||
// handle type (for 'type safety' - can't use a texture handle as a sound)
|
||||
|
||||
|
||||
//
|
||||
// rationale: we could use the destructor passed to h_alloc to identify
|
||||
// the handle, but it's good to have a list of all types, and we avoid having
|
||||
// to create empty destructors for handle types that wouldn't need them.
|
||||
// finally, we save memory - this fits in a few bits, vs. needing a pointer.
|
||||
|
||||
// registering extension for each module is bad - some may use many
|
||||
// (e.g. texture - many formats).
|
||||
// handle manager shouldn't know about handle types
|
||||
|
||||
/*
|
||||
enum H_Type
|
||||
{
|
||||
H_Mem = 1,
|
||||
H_ZArchive = 2,
|
||||
H_ZFile = 3,
|
||||
H_VFile = 4,
|
||||
H_VRead = 5,
|
||||
|
||||
H_Tex = 6,
|
||||
H_Font = 7,
|
||||
H_Sound = 8,
|
||||
|
||||
NUM_HANDLE_TYPES
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
///xxx advantage of manual vtbl:
|
||||
@ -301,7 +278,7 @@ but- has to handle variable params, a bit ugly
|
||||
struct H_VTbl
|
||||
{
|
||||
void (*init)(void* user, va_list);
|
||||
LibError (*reload)(void* user, const char* fn, Handle);
|
||||
LibError (*reload)(void* user, const VfsPath& pathname, Handle);
|
||||
void (*dtor)(void* user);
|
||||
LibError (*validate)(const void* user);
|
||||
LibError (*to_string)(const void* user, char* buf);
|
||||
@ -314,14 +291,14 @@ typedef H_VTbl* H_Type;
|
||||
#define H_TYPE_DEFINE(type)\
|
||||
/* forward decls */\
|
||||
static void type##_init(type*, va_list);\
|
||||
static LibError type##_reload(type*, const char*, Handle);\
|
||||
static LibError type##_reload(type*, const VfsPath&, Handle);\
|
||||
static void type##_dtor(type*);\
|
||||
static LibError type##_validate(const type*);\
|
||||
static LibError type##_to_string(const type*, char* buf);\
|
||||
static H_VTbl V_##type =\
|
||||
{\
|
||||
(void (*)(void*, va_list))type##_init,\
|
||||
(LibError (*)(void*, const char*, Handle))type##_reload,\
|
||||
(LibError (*)(void*, const VfsPath&, Handle))type##_reload,\
|
||||
(void (*)(void*))type##_dtor,\
|
||||
(LibError (*)(const void*))type##_validate,\
|
||||
(LibError (*)(const void*, char*))type##_to_string,\
|
||||
@ -382,10 +359,6 @@ enum
|
||||
// not cached, and will never reuse a previous instance
|
||||
RES_UNIQUE = RES_NO_CACHE|0x10,
|
||||
|
||||
// the resource isn't backed by a file. the fn parameter is treated as the search key (uintptr_t)
|
||||
// currently only used by mem manager
|
||||
RES_KEY = 0x08,
|
||||
|
||||
// object is requesting it never be reloaded (e.g. because it's not
|
||||
// backed by a file)
|
||||
RES_DISALLOW_RELOAD = 0x20
|
||||
@ -403,7 +376,7 @@ const size_t H_STRING_LEN = 256;
|
||||
//// user_size is checked to make sure the user data fits in the handle data space.
|
||||
// dtor is associated with type and called when the object is freed.
|
||||
// handle data is initialized to 0; optionally, a pointer to it is returned.
|
||||
extern Handle h_alloc(H_Type type, const char* fn, uint flags = 0, ...);
|
||||
extern Handle h_alloc(H_Type type, const VfsPath& pathname, uint flags = 0, ...);
|
||||
extern LibError h_free(Handle& h, H_Type type);
|
||||
|
||||
|
||||
@ -419,10 +392,10 @@ extern Handle h_find(H_Type type, uintptr_t key);
|
||||
// prefer using H_DEREF or H_USER_DATA.
|
||||
extern void* h_user_data(Handle h, H_Type type);
|
||||
|
||||
extern const char* h_filename(Handle h);
|
||||
extern VfsPath h_filename(Handle h);
|
||||
|
||||
|
||||
extern LibError h_reload(const char* fn);
|
||||
extern LibError h_reload(const VfsPath& pathname);
|
||||
|
||||
// force the resource to be freed immediately, even if cached.
|
||||
// tag is not checked - this allows the first Handle returned
|
||||
|
@ -11,6 +11,8 @@
|
||||
#ifndef INCLUDED_HANDLE
|
||||
#define INCLUDED_HANDLE
|
||||
|
||||
#include "lib/file/vfs/vfs_path.h"
|
||||
|
||||
// 0 = invalid handle value; < 0 is an error code.
|
||||
// 64 bits, because we want tags to remain unique: tag overflow may
|
||||
// let handle use errors slip through, or worse, cause spurious errors.
|
||||
|
@ -765,49 +765,29 @@ if(sd->o) ogg_release(sd->o);
|
||||
// but that load failed).
|
||||
static void hsd_list_add(Handle hsd);
|
||||
|
||||
static LibError SndData_reload(SndData * sd, const char* fn, Handle hsd)
|
||||
static LibError SndData_reload(SndData* sd, const VfsPath& pathname, Handle hsd)
|
||||
{
|
||||
//
|
||||
// detect sound format by checking file extension
|
||||
//
|
||||
|
||||
enum FileType
|
||||
{
|
||||
FT_OGG
|
||||
}
|
||||
file_type;
|
||||
|
||||
const char* ext = path_extension(fn);
|
||||
// .. OGG (data will be passed directly to OpenAL)
|
||||
if(!strcasecmp(ext, "ogg"))
|
||||
{
|
||||
#ifdef OGG_HACK
|
||||
#else
|
||||
// first use of OGG: check if OpenAL extension is available.
|
||||
// note: this is required! OpenAL does its init here.
|
||||
static int ogg_supported = -1;
|
||||
if(ogg_supported == -1)
|
||||
ogg_supported = alIsExtensionPresent((ALubyte*)"AL_EXT_vorbis")? 1 : 0;
|
||||
if(!ogg_supported)
|
||||
WARN_RETURN(ERR::NO_SYS);
|
||||
|
||||
sd->al_fmt = AL_FORMAT_VORBIS_EXT;
|
||||
sd->al_freq = 0;
|
||||
#endif
|
||||
|
||||
file_type = FT_OGG;
|
||||
}
|
||||
// .. unknown extension
|
||||
else
|
||||
WARN_RETURN(ERR::FAIL);
|
||||
|
||||
// note: WAV is no longer supported. writing our own loader is infeasible
|
||||
// currently only supports OGG; WAV is no longer supported. writing our own loader is infeasible
|
||||
// due to a seriously watered down spec with many incompatible variants.
|
||||
// pulling in an external library (e.g. freealut) is deemed not worth the
|
||||
// effort - OGG should be better in all cases.
|
||||
|
||||
#ifdef OGG_HACK
|
||||
#else
|
||||
// first use of OGG: check if OpenAL extension is available.
|
||||
// note: this is required! OpenAL does its init here.
|
||||
static int ogg_supported = -1;
|
||||
if(ogg_supported == -1)
|
||||
ogg_supported = alIsExtensionPresent((ALubyte*)"AL_EXT_vorbis")? 1 : 0;
|
||||
if(!ogg_supported)
|
||||
WARN_RETURN(ERR::NO_SYS);
|
||||
|
||||
sd->al_fmt = AL_FORMAT_VORBIS_EXT;
|
||||
sd->al_freq = 0;
|
||||
#endif
|
||||
|
||||
shared_ptr<u8> file; size_t file_size;
|
||||
RETURN_ERR(g_VFS->LoadFile(fn, file, file_size));
|
||||
RETURN_ERR(g_VFS->LoadFile(pathname, file, file_size));
|
||||
|
||||
ALvoid* al_data = (ALvoid*)file.get();
|
||||
ALsizei al_size = (ALsizei)file_size;
|
||||
@ -815,29 +795,22 @@ static LibError SndData_reload(SndData * sd, const char* fn, Handle hsd)
|
||||
#ifdef OGG_HACK
|
||||
std::vector<u8> data;
|
||||
data.reserve(500000);
|
||||
if(file_type == FT_OGG)
|
||||
sd->o = ogg_create();
|
||||
ogg_give_raw(sd->o, file.get(), file_size);
|
||||
ogg_open(sd->o, sd->al_fmt, sd->al_freq);
|
||||
size_t datasize=0;
|
||||
size_t bytes_read;
|
||||
do
|
||||
{
|
||||
sd->o = ogg_create();
|
||||
ogg_give_raw(sd->o, file.get(), file_size);
|
||||
ogg_open(sd->o, sd->al_fmt, sd->al_freq);
|
||||
size_t datasize=0;
|
||||
size_t bytes_read;
|
||||
do
|
||||
{
|
||||
const size_t bufsize = 32*KiB;
|
||||
char buf[bufsize];
|
||||
bytes_read = ogg_read(sd->o, buf, bufsize);
|
||||
data.insert(data.end(), &buf[0], &buf[bytes_read]);
|
||||
datasize += bytes_read;
|
||||
}
|
||||
while(bytes_read > 0);
|
||||
al_data = &data[0];
|
||||
al_size = (ALsizei)datasize;
|
||||
}
|
||||
else
|
||||
{
|
||||
sd->o = NULL;
|
||||
const size_t bufsize = 32*KiB;
|
||||
char buf[bufsize];
|
||||
bytes_read = ogg_read(sd->o, buf, bufsize);
|
||||
data.insert(data.end(), &buf[0], &buf[bytes_read]);
|
||||
datasize += bytes_read;
|
||||
}
|
||||
while(bytes_read > 0);
|
||||
al_data = &data[0];
|
||||
al_size = (ALsizei)datasize;
|
||||
#endif
|
||||
|
||||
sd->al_buf = al_buf_alloc(al_data, al_size, sd->al_fmt, sd->al_freq);
|
||||
@ -859,7 +832,7 @@ static LibError SndData_validate(const SndData * sd)
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
static LibError SndData_to_string(const SndData * sd, char * buf)
|
||||
static LibError SndData_to_string(const SndData* sd, char* buf)
|
||||
{
|
||||
const char* type = "clip";
|
||||
snprintf(buf, H_STRING_LEN, "%s; al_buf=%d", type, sd->al_buf);
|
||||
@ -870,18 +843,17 @@ static LibError SndData_to_string(const SndData * sd, char * buf)
|
||||
/**
|
||||
* open and return a handle to a sound file's data.
|
||||
*
|
||||
* @param fn VFS filename
|
||||
* @param is_stream (default false) indicates whether this file should be
|
||||
* streamed in (opening is faster, it won't be kept in memory, but
|
||||
* only one instance can be open at a time; makes sense for large music files)
|
||||
* or loaded immediately.
|
||||
* @return Handle or LibError on failure
|
||||
*/
|
||||
static Handle snd_data_load(const char* fn, bool is_stream)
|
||||
static Handle snd_data_load(const VfsPath& pathname, bool is_stream)
|
||||
{
|
||||
debug_assert(!is_stream); // no longer supported
|
||||
|
||||
return h_alloc(H_SndData, fn);
|
||||
return h_alloc(H_SndData, pathname);
|
||||
}
|
||||
|
||||
|
||||
@ -1227,7 +1199,7 @@ static void VSrc_dtor(VSrc* vs)
|
||||
(void)snd_data_free(vs->hsd);
|
||||
}
|
||||
|
||||
static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs)
|
||||
static LibError VSrc_reload(VSrc* vs, const VfsPath& pathname, Handle hvs)
|
||||
{
|
||||
// cannot wait till play(), need to init here:
|
||||
// must load OpenAL so that snd_data_load can check for OGG extension.
|
||||
@ -1238,34 +1210,24 @@ static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs)
|
||||
// .. catch genuine errors during init.
|
||||
RETURN_ERR(err);
|
||||
|
||||
//
|
||||
// if extension is "txt", fn is a definition file containing the
|
||||
// sound file name and its gain; otherwise, read directly from fn
|
||||
// and assume default gain (1.0).
|
||||
//
|
||||
VfsPath dataPathname;
|
||||
|
||||
const char* snd_fn; // actual sound file name
|
||||
std::string snd_fn_s;
|
||||
// extracted from stringstream;
|
||||
// declare here so that it doesn't go out of scope below.
|
||||
|
||||
const char* ext = path_extension(fn);
|
||||
if(!strcasecmp(ext, "txt"))
|
||||
// pathname is a definition file containing the data file name and
|
||||
// its gain.
|
||||
if(fs::extension((const fs::path&)pathname) == ".txt")
|
||||
{
|
||||
shared_ptr<u8> buf; size_t size;
|
||||
RETURN_ERR(g_VFS->LoadFile(fn, buf, size));
|
||||
RETURN_ERR(g_VFS->LoadFile(pathname, buf, size));
|
||||
std::istringstream def(std::string((char*)buf.get(), (int)size));
|
||||
|
||||
float gain_percent;
|
||||
def >> snd_fn_s;
|
||||
def >> gain_percent;
|
||||
|
||||
snd_fn = snd_fn_s.c_str();
|
||||
vs->gain = gain_percent / 100.0f;
|
||||
def >> dataPathname;
|
||||
def >> vs->gain;
|
||||
vs->gain /= 100.0f; // is stored as percent
|
||||
}
|
||||
// read the sound file directly and assume default gain (1.0).
|
||||
else
|
||||
{
|
||||
snd_fn = fn;
|
||||
dataPathname = pathname;
|
||||
vs->gain = 1.0f;
|
||||
}
|
||||
|
||||
@ -1277,7 +1239,7 @@ static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs)
|
||||
// needed so we can snd_free ourselves when done playing.
|
||||
|
||||
bool is_stream = (vs->flags & VS_IS_STREAM) != 0;
|
||||
vs->hsd = snd_data_load(snd_fn, is_stream);
|
||||
vs->hsd = snd_data_load(dataPathname, is_stream);
|
||||
RETURN_ERR(vs->hsd);
|
||||
|
||||
return INFO::OK;
|
||||
@ -1309,7 +1271,7 @@ static LibError VSrc_to_string(const VSrc* vs, char * buf)
|
||||
/**
|
||||
* open and return a handle to a sound instance.
|
||||
*
|
||||
* @param snd_fn VFS filename. if a text file (extension ".txt"),
|
||||
* @param pathname. if a text file (extension ".txt"),
|
||||
* it is assumed to be a definition file containing the
|
||||
* sound file name and its gain (0.0 .. 1.0).
|
||||
* otherwise, it is taken to be the sound file name and
|
||||
@ -1320,14 +1282,14 @@ static LibError VSrc_to_string(const VSrc* vs, char * buf)
|
||||
* or loaded immediately.
|
||||
* @return Handle or LibError on failure
|
||||
*/
|
||||
Handle snd_open(const char* snd_fn, bool is_stream)
|
||||
Handle snd_open(const VfsPath& pathname, bool is_stream)
|
||||
{
|
||||
uint flags = 0;
|
||||
if(is_stream)
|
||||
flags |= VS_IS_STREAM;
|
||||
// note: RES_UNIQUE forces each instance to get a new resource
|
||||
// (which is of course what we want).
|
||||
return h_alloc(H_VSrc, snd_fn, RES_UNIQUE, flags);
|
||||
return h_alloc(H_VSrc, pathname, RES_UNIQUE, flags);
|
||||
}
|
||||
|
||||
|
||||
|
@ -153,7 +153,7 @@ extern LibError snd_set_master_gain(float gain);
|
||||
* open and return a handle to a sound instance.
|
||||
* this loads the sound data and makes it ready for other snd_* APIs.
|
||||
*
|
||||
* @param snd_fn input filename. if a text file (extension "txt"), it is
|
||||
* @param pathname. if a text file (extension ".txt"), it is
|
||||
* assumed to be a definition file containing the sound file name and
|
||||
* its gain (0.0 .. 1.0).
|
||||
* otherwise, it is taken to be the sound file name and
|
||||
@ -164,7 +164,7 @@ extern LibError snd_set_master_gain(float gain);
|
||||
* only one instance can be open at a time.
|
||||
* @return Handle or LibError
|
||||
**/
|
||||
extern Handle snd_open(const char* snd_fn, bool stream = false);
|
||||
extern Handle snd_open(const VfsPath& name, bool stream = false);
|
||||
|
||||
/**
|
||||
* close the sound instance. if it was playing, it will be stopped.
|
||||
|
@ -14,10 +14,11 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/path_util.h"
|
||||
#include "win.h"
|
||||
#include "wutil.h"
|
||||
|
||||
#include "lib/file/io/io.h" // io_Allocate
|
||||
|
||||
#if MSC_VERSION
|
||||
#pragma comment(lib, "version.lib") // DLL version
|
||||
#endif
|
||||
@ -25,31 +26,34 @@
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// helper function that does all the work; caller wraps it and takes care of
|
||||
// undoing various operations if we fail midway.
|
||||
static LibError get_ver_impl(const char* module_path, char* out_ver, size_t out_ver_len, u8*& mem)
|
||||
static LibError ReadVersionString(const OsPath& modulePathname_, char* out_ver, size_t out_ver_len)
|
||||
{
|
||||
WIN_SAVE_LAST_ERROR; // GetFileVersion*, Ver*
|
||||
WinScopedDisableWow64Redirection noRedirect;
|
||||
|
||||
const std::string modulePathname = modulePathname_.external_file_string();
|
||||
|
||||
#ifndef NDEBUG
|
||||
// make sure the file exists (rules out that problem as a cause of
|
||||
// GetFileVersionInfoSize failing, since it doesn't SetLastError)
|
||||
HMODULE hModule = LoadLibraryEx(module_path, 0, LOAD_LIBRARY_AS_DATAFILE);
|
||||
HMODULE hModule = LoadLibraryEx(modulePathname.c_str(), 0, LOAD_LIBRARY_AS_DATAFILE);
|
||||
debug_assert(hModule != 0);
|
||||
FreeLibrary(hModule);
|
||||
#endif
|
||||
|
||||
// determine size of and allocate memory for version information.
|
||||
DWORD unused;
|
||||
const DWORD ver_size = GetFileVersionInfoSize(module_path, &unused);
|
||||
const DWORD ver_size = GetFileVersionInfoSize(modulePathname.c_str(), &unused);
|
||||
if(!ver_size)
|
||||
WARN_RETURN(ERR::FAIL);
|
||||
mem = new u8[ver_size];
|
||||
|
||||
if(!GetFileVersionInfo(module_path, 0, ver_size, mem))
|
||||
shared_ptr<u8> mem = io_Allocate(ver_size);
|
||||
if(!GetFileVersionInfo(modulePathname.c_str(), 0, ver_size, mem.get()))
|
||||
WARN_RETURN(ERR::FAIL);
|
||||
|
||||
u16* lang; // -> 16 bit language ID, 16 bit codepage
|
||||
uint lang_len;
|
||||
const BOOL ok = VerQueryValue(mem, "\\VarFileInfo\\Translation", (void**)&lang, &lang_len);
|
||||
const BOOL ok = VerQueryValue(mem.get(), "\\VarFileInfo\\Translation", (void**)&lang, &lang_len);
|
||||
if(!ok || !lang || lang_len != 4)
|
||||
WARN_RETURN(ERR::FAIL);
|
||||
|
||||
@ -57,34 +61,13 @@ static LibError get_ver_impl(const char* module_path, char* out_ver, size_t out_
|
||||
sprintf(subblock, "\\StringFileInfo\\%04X%04X\\FileVersion", lang[0], lang[1]);
|
||||
const char* in_ver;
|
||||
uint in_ver_len;
|
||||
if(!VerQueryValue(mem, subblock, (void**)&in_ver, &in_ver_len))
|
||||
if(!VerQueryValue(mem.get(), subblock, (void**)&in_ver, &in_ver_len))
|
||||
WARN_RETURN(ERR::FAIL);
|
||||
|
||||
strcpy_s(out_ver, out_ver_len, in_ver);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
// get version information for the specified DLL.
|
||||
static LibError get_ver(const char* module_path, char* out_ver, size_t out_ver_len)
|
||||
{
|
||||
LibError ret;
|
||||
|
||||
WIN_SAVE_LAST_ERROR; // GetFileVersion*, Ver*
|
||||
{
|
||||
PVOID wasRedirectionEnabled;
|
||||
wutil_DisableWow64Redirection(wasRedirectionEnabled);
|
||||
{
|
||||
u8* mem = 0;
|
||||
ret = get_ver_impl(module_path, out_ver, out_ver_len, mem);
|
||||
delete[] mem;
|
||||
}
|
||||
wutil_RevertWow64Redirection(wasRedirectionEnabled);
|
||||
}
|
||||
WIN_RESTORE_LAST_ERROR;
|
||||
|
||||
if(ret != INFO::OK)
|
||||
out_ver[0] = '\0';
|
||||
return ret;
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
//
|
||||
@ -108,41 +91,35 @@ void wdll_ver_list_init(char* buf, size_t chars)
|
||||
// name should preferably be the complete path to DLL, to make sure
|
||||
// we don't inadvertently load another one on the library search path.
|
||||
// we add the .dll extension if necessary.
|
||||
LibError wdll_ver_list_add(const char* name)
|
||||
LibError wdll_ver_list_add(const OsPath& pathname)
|
||||
{
|
||||
// not be called before wdll_ver_list_init or after failure
|
||||
if(!ver_list_pos)
|
||||
WARN_RETURN(ERR::LOGIC);
|
||||
|
||||
// some driver names are stored in the registry without .dll extension.
|
||||
// if necessary, copy to new buffer and add it there.
|
||||
// note: do not change extension if present; some drivers have a
|
||||
// ".sys" extension, so always appending ".dll" is incorrect.
|
||||
char buf[MAX_PATH];
|
||||
const char* dll_name = name;
|
||||
const char* ext = path_extension(name);
|
||||
if(ext[0] == '\0') // no extension
|
||||
{
|
||||
snprintf(buf, ARRAY_SIZE(buf), "%s.dll", name);
|
||||
dll_name = buf;
|
||||
}
|
||||
// pathname may not have an extension (e.g. driver names from the
|
||||
// registry). note that always appending ".dll" would be incorrect
|
||||
// since some have ".sys" extension.
|
||||
OsPath modulePathname(pathname);
|
||||
if(fs::extension(modulePathname).empty())
|
||||
modulePathname = fs::change_extension(modulePathname, ".dll");
|
||||
|
||||
// read file version.
|
||||
char dll_ver[500] = "unknown"; // enclosed in () below
|
||||
(void)get_ver(dll_name, dll_ver, sizeof(dll_ver));
|
||||
// if this fails, default is already set and we don't want to abort.
|
||||
// (note: we can ignore the return value since the default
|
||||
// text has already been set)
|
||||
char versionString[500] = "unknown"; // enclosed in () below
|
||||
(void)ReadVersionString(modulePathname, versionString, ARRAY_SIZE(versionString));
|
||||
|
||||
// reserve enough room for subsequent comma and "..." strings.
|
||||
const ssize_t max_chars_to_write = (ssize_t)ver_list_chars - (ver_list_pos-ver_list_buf) - 10;
|
||||
// reserves enough room for subsequent comma and "..." strings.
|
||||
|
||||
|
||||
// not first time: prepend comma to string (room was reserved above).
|
||||
if(ver_list_pos != ver_list_buf)
|
||||
ver_list_pos += sprintf(ver_list_pos, ", ");
|
||||
|
||||
// extract filename.
|
||||
const char* dll_fn = path_name_only(dll_name);
|
||||
|
||||
int len = snprintf(ver_list_pos, max_chars_to_write, "%s (%s)", dll_fn, dll_ver);
|
||||
const std::string moduleName(modulePathname.leaf());
|
||||
int len = snprintf(ver_list_pos, max_chars_to_write, "%s (%s)", moduleName.c_str(), versionString);
|
||||
// success
|
||||
if(len > 0)
|
||||
{
|
||||
|
@ -11,6 +11,8 @@
|
||||
#ifndef INCLUDED_WDLL_VER
|
||||
#define INCLUDED_WDLL_VER
|
||||
|
||||
#include "lib/os_path.h"
|
||||
|
||||
// WARNING: not re-entrant or thread-safe!
|
||||
|
||||
// set output buffer into which DLL names and their versions will be written.
|
||||
@ -21,6 +23,6 @@ extern void wdll_ver_list_init(char* buf, size_t chars);
|
||||
// name should preferably be the complete path to DLL, to make sure
|
||||
// we don't inadvertently load another one on the library search path.
|
||||
// we add the .dll extension if necessary.
|
||||
extern LibError wdll_ver_list_add(const char* name);
|
||||
extern LibError wdll_ver_list_add(const OsPath& name);
|
||||
|
||||
#endif // #ifndef INCLUDED_WDLL_VER
|
||||
|
@ -71,7 +71,7 @@ static u16 cur_ramp[3][256];
|
||||
|
||||
|
||||
// ramp: 8.8 fixed point
|
||||
static LibError calc_gamma_ramp(float gamma, u16* ramp)
|
||||
static void calc_gamma_ramp(float gamma, u16* ramp)
|
||||
{
|
||||
// assume identity if invalid
|
||||
if(gamma <= 0.0f)
|
||||
@ -82,7 +82,7 @@ static LibError calc_gamma_ramp(float gamma, u16* ramp)
|
||||
{
|
||||
for(u16 i = 0; i < 256; i++)
|
||||
ramp[i] = (i << 8);
|
||||
return INFO::OK;
|
||||
return;
|
||||
}
|
||||
|
||||
const double inv_gamma = 1.0 / gamma;
|
||||
@ -94,8 +94,6 @@ static LibError calc_gamma_ramp(float gamma, u16* ramp)
|
||||
// need a temp variable to disambiguate pow() argument type.
|
||||
ramp[i] = u16_from_double(pow(frac, inv_gamma));
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
@ -124,11 +122,9 @@ int SDL_SetGamma(float r, float g, float b)
|
||||
if(!GetDeviceGammaRamp(hDC, org_ramp))
|
||||
return -1;
|
||||
|
||||
LibError err1 = calc_gamma_ramp(r, cur_ramp[0]);
|
||||
LibError err2 = calc_gamma_ramp(g, cur_ramp[1]);
|
||||
LibError err3 = calc_gamma_ramp(b, cur_ramp[2]);
|
||||
if(err1 != INFO::OK || err2 != INFO::OK || err3 != INFO::OK)
|
||||
return -1;
|
||||
calc_gamma_ramp(r, cur_ramp[0]);
|
||||
calc_gamma_ramp(g, cur_ramp[1]);
|
||||
calc_gamma_ramp(b, cur_ramp[2]);
|
||||
|
||||
if(!SetDeviceGammaRamp(hDC, cur_ramp))
|
||||
return -1;
|
||||
|
@ -17,72 +17,45 @@
|
||||
#include <set>
|
||||
|
||||
#include "lib/path_util.h"
|
||||
#include "wposix/wfilesystem.h" // see add_oal_dlls_in_dir
|
||||
#include "wdll_ver.h"
|
||||
#include "win.h"
|
||||
#include "wutil.h"
|
||||
#include "wmi.h"
|
||||
|
||||
|
||||
// indicate if this filename corresponds to the OpenAL DLL name format.
|
||||
// (matches "*oal.dll" and "*OpenAL*", as with OpenAL router's search)
|
||||
static LibError IsOpenAlDllName(const char* name)
|
||||
static LibError IsOpenAlDllName(const std::string& name)
|
||||
{
|
||||
const size_t len = strlen(name);
|
||||
|
||||
if(len >= 7 && !strcasecmp(name+len-7, "oal.dll"))
|
||||
return true;
|
||||
|
||||
if(strstr(name, "OpenAL") != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
// (matches "*oal.dll" and "*OpenAL*", as with OpenAL router's search)
|
||||
return name.find("oal.dll") != std::string::npos || name.find("OpenAL") != std::string::npos;
|
||||
}
|
||||
|
||||
// ensures each OpenAL DLL is only listed once (even if present in several
|
||||
// directories on our search path).
|
||||
typedef std::set<std::string> StringSet;
|
||||
|
||||
// find all OpenAL DLLs in a dir (via readdir and IsOpenAlDll).
|
||||
// find all OpenAL DLLs in a dir.
|
||||
// call in library search order (exe dir, then win sys dir); otherwise,
|
||||
// DLLs in the executable's starting directory hide those of the
|
||||
// same name in the system directory.
|
||||
static LibError add_oal_dlls_in_dir(const char* path, StringSet* dlls)
|
||||
static void add_oal_dlls_in_dir(const OsPath& path, StringSet& dlls)
|
||||
{
|
||||
// note: wdll_ver_list_add requires the full DLL path but readdir only
|
||||
// gives us the name. for efficiency, we append this via PathPackage.
|
||||
PathPackage pp;
|
||||
RETURN_ERR(path_package_set_dir(&pp, path));
|
||||
|
||||
// note: we can't use the dir_open/DirIterator interface because it
|
||||
// expects a portable (relative) path and <path> is absolute. using
|
||||
// POSIX opendir is slightly more complex but works.
|
||||
errno = 0;
|
||||
DIR* dir = opendir(path);
|
||||
if(!dir)
|
||||
return LibError_from_errno();
|
||||
|
||||
for(;;) // instead of while to avoid warning
|
||||
for(OsDirectoryIterator it(path); it != OsDirectoryIterator(); ++it)
|
||||
{
|
||||
dirent* ent = readdir(dir);
|
||||
if(!ent)
|
||||
break;
|
||||
if(!fs::is_regular(it->status()))
|
||||
continue;
|
||||
|
||||
if(!IsOpenAlDllName(ent->d_name))
|
||||
const OsPath& pathname = it->path();
|
||||
const std::string& name = pathname.leaf();
|
||||
if(!IsOpenAlDllName(name))
|
||||
continue;
|
||||
|
||||
// already in StringSet (i.e. has already been wdll_ver_list_add-ed)
|
||||
std::pair<StringSet::iterator, bool> ret = dlls->insert(ent->d_name);
|
||||
std::pair<StringSet::iterator, bool> ret = dlls.insert(name);
|
||||
if(!ret.second) // insert failed - element already there
|
||||
continue;
|
||||
|
||||
(void)path_package_append_file(&pp, ent->d_name);
|
||||
(void)wdll_ver_list_add(pp.path);
|
||||
(void)wdll_ver_list_add(pathname);
|
||||
}
|
||||
|
||||
int ret = closedir(dir);
|
||||
debug_assert(ret == 0);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
@ -149,7 +122,7 @@ LibError win_get_snd_info()
|
||||
if(wutil_WindowsVersion() < WUTIL_VERSION_VISTA)
|
||||
(void)wdll_ver_list_add(GetDirectSoundDriverPath());
|
||||
StringSet dlls; // ensures uniqueness
|
||||
(void)add_oal_dlls_in_dir(win_exe_dir, &dlls);
|
||||
(void)add_oal_dlls_in_dir(win_sys_dir, &dlls);
|
||||
(void)add_oal_dlls_in_dir(win_exe_dir, dlls);
|
||||
(void)add_oal_dlls_in_dir(win_sys_dir, dlls);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
@ -398,7 +398,8 @@ bool wutil_IsWow64()
|
||||
return isWow64;
|
||||
}
|
||||
|
||||
void wutil_DisableWow64Redirection(void*& wasRedirectionEnabled)
|
||||
|
||||
WinScopedDisableWow64Redirection::WinScopedDisableWow64Redirection()
|
||||
{
|
||||
// note: don't just check if the function pointers are valid. 32-bit
|
||||
// Vista includes them but isn't running Wow64, so calling the functions
|
||||
@ -406,15 +407,15 @@ void wutil_DisableWow64Redirection(void*& wasRedirectionEnabled)
|
||||
// more need to verify the pointers (their existence is implied).
|
||||
if(!wutil_IsWow64())
|
||||
return;
|
||||
BOOL ok = pWow64DisableWow64FsRedirection(&wasRedirectionEnabled);
|
||||
BOOL ok = pWow64DisableWow64FsRedirection(&m_wasRedirectionEnabled);
|
||||
WARN_IF_FALSE(ok);
|
||||
}
|
||||
|
||||
void wutil_RevertWow64Redirection(void* wasRedirectionEnabled)
|
||||
WinScopedDisableWow64Redirection::~WinScopedDisableWow64Redirection()
|
||||
{
|
||||
if(!wutil_IsWow64())
|
||||
return;
|
||||
BOOL ok = pWow64RevertWow64FsRedirection(wasRedirectionEnabled);
|
||||
BOOL ok = pWow64RevertWow64FsRedirection(m_wasRedirectionEnabled);
|
||||
WARN_IF_FALSE(ok);
|
||||
}
|
||||
|
||||
|
@ -142,8 +142,16 @@ extern uint wutil_WindowsVersion();
|
||||
//
|
||||
|
||||
extern bool wutil_IsWow64();
|
||||
extern void wutil_DisableWow64Redirection(void*& wasRedirectionEnabled);
|
||||
extern void wutil_RevertWow64Redirection(void* wasRedirectionEnabled);
|
||||
|
||||
class WinScopedDisableWow64Redirection
|
||||
{
|
||||
public:
|
||||
WinScopedDisableWow64Redirection();
|
||||
~WinScopedDisableWow64Redirection();
|
||||
|
||||
private:
|
||||
void* m_wasRedirectionEnabled;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
@ -49,16 +49,6 @@ class TestPathUtil : public CxxTest::TestSuite
|
||||
TS_ASSERT_STR_EQUALS(result, correct_result);
|
||||
}
|
||||
|
||||
void TEST_PATH_PACKAGE(const char* path, const char* fn,
|
||||
const char* correct_result)
|
||||
{
|
||||
PathPackage pp;
|
||||
TS_ASSERT_OK(path_package_set_dir(&pp, path));
|
||||
TS_ASSERT_OK(path_package_append_file(&pp, fn));
|
||||
TS_ASSERT_STR_EQUALS(pp.path, correct_result);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
void test_subpath()
|
||||
@ -195,22 +185,4 @@ public:
|
||||
TEST_PATH_EXT("c", ""); // no extension
|
||||
TEST_PATH_EXT("", ""); // empty
|
||||
}
|
||||
|
||||
// testing path_foreach_component is difficult; currently skipped.
|
||||
|
||||
void test_path_package()
|
||||
{
|
||||
// normal
|
||||
TEST_PATH_PACKAGE("a/b", "c", "a/b/c");
|
||||
// nonportable slash
|
||||
TEST_PATH_PACKAGE("a\\b", "c", "a\\b/c");
|
||||
// slash already present
|
||||
TEST_PATH_PACKAGE("a/b/", "c", "a/b/c");
|
||||
// nonportable slash already present
|
||||
TEST_PATH_PACKAGE("a\\b\\", "c", "a\\b\\c");
|
||||
// mixed slashes
|
||||
TEST_PATH_PACKAGE("a/b\\c", "d", "a/b\\c/d");
|
||||
// mixed slashes (2)
|
||||
TEST_PATH_PACKAGE("a\\b/c", "d", "a\\b/c/d");
|
||||
}
|
||||
};
|
||||
|
@ -20,10 +20,6 @@
|
||||
|
||||
#include "tex_codec.h"
|
||||
|
||||
#include "lib/file/vfs/vfs.h"
|
||||
#include "lib/file/io/io_internal.h"
|
||||
extern PIVFS g_VFS;
|
||||
|
||||
|
||||
ERROR_ASSOCIATE(ERR::TEX_FMT_INVALID, "Invalid/unsupported texture format", -1);
|
||||
ERROR_ASSOCIATE(ERR::TEX_INVALID_COLOR_TYPE, "Invalid color type", -1);
|
||||
@ -479,7 +475,8 @@ bool tex_is_known_extension(const char* filename)
|
||||
{
|
||||
const TexCodecVTbl* dummy;
|
||||
// found codec for it => known extension
|
||||
if(tex_codec_for_filename(filename, &dummy) == INFO::OK)
|
||||
const std::string extension = fs::extension(filename);
|
||||
if(tex_codec_for_filename(extension, &dummy) == INFO::OK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@ -504,7 +501,7 @@ LibError tex_wrap(uint w, uint h, uint bpp, uint flags, shared_ptr<u8> data, siz
|
||||
t->bpp = bpp;
|
||||
t->flags = flags;
|
||||
t->data = data;
|
||||
t->dataSize = w * h * bpp / 8;
|
||||
t->dataSize = ofs + w*h*bpp/8;
|
||||
t->ofs = ofs;
|
||||
|
||||
CHECK_TEX(t);
|
||||
@ -577,7 +574,9 @@ size_t tex_img_size(const Tex* t)
|
||||
size_t tex_hdr_size(const char* fn)
|
||||
{
|
||||
const TexCodecVTbl* c;
|
||||
CHECK_ERR(tex_codec_for_filename(fn, &c));
|
||||
|
||||
const std::string extension = fs::extension(fn);
|
||||
CHECK_ERR(tex_codec_for_filename(extension, &c));
|
||||
return c->hdr_size(0);
|
||||
}
|
||||
|
||||
@ -622,11 +621,13 @@ LibError tex_decode(shared_ptr<u8> data, size_t data_size, Tex* t)
|
||||
|
||||
flip_to_global_orientation(t);
|
||||
|
||||
CHECK_TEX(t);
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
LibError tex_encode(Tex* t, const char* fn, DynArray* da)
|
||||
LibError tex_encode(Tex* t, const std::string& extension, DynArray* da)
|
||||
{
|
||||
CHECK_TEX(t);
|
||||
CHECK_ERR(tex_validate_plain_format(t->bpp, t->flags));
|
||||
@ -640,7 +641,7 @@ LibError tex_encode(Tex* t, const char* fn, DynArray* da)
|
||||
RETURN_ERR(da_alloc(da, max_out_size));
|
||||
|
||||
const TexCodecVTbl* c;
|
||||
CHECK_ERR(tex_codec_for_filename(fn, &c));
|
||||
CHECK_ERR(tex_codec_for_filename(extension, &c));
|
||||
|
||||
// encode into <da>
|
||||
LibError err = c->encode(t, da);
|
||||
@ -652,51 +653,3 @@ LibError tex_encode(Tex* t, const char* fn, DynArray* da)
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
// load the specified image from file into the given Tex object.
|
||||
// currently supports BMP, TGA, JPG, JP2, PNG, DDS.
|
||||
LibError tex_load(const char* fn, Tex* t)
|
||||
{
|
||||
// load file
|
||||
shared_ptr<u8> file; size_t file_size;
|
||||
RETURN_ERR(g_VFS->LoadFile(fn, file, file_size));
|
||||
|
||||
LibError ret = tex_decode(file, file_size, t);
|
||||
if(ret < 0)
|
||||
{
|
||||
tex_free(t);
|
||||
RETURN_ERR(ret);
|
||||
}
|
||||
|
||||
// do not free data! it either still holds the image data (i.e. texture
|
||||
// wasn't compressed) or was replaced by a new buffer for the image data.
|
||||
|
||||
CHECK_TEX(t);
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
|
||||
// write the specified texture to disk.
|
||||
// note: <t> cannot be made const because the image may have to be
|
||||
// transformed to write it out in the format determined by <fn>'s extension.
|
||||
LibError tex_write(Tex* t, const char* fn)
|
||||
{
|
||||
DynArray da;
|
||||
RETURN_ERR(tex_encode(t, fn, &da));
|
||||
|
||||
// write to disk
|
||||
LibError ret = INFO::OK;
|
||||
{
|
||||
(void)da_set_size(&da, round_up(da.cur_size, BLOCK_SIZE));
|
||||
shared_ptr<u8> file(da.base, DummyDeleter<u8>());
|
||||
const ssize_t bytes_written = g_VFS->CreateFile(fn, file, da.pos);
|
||||
if(bytes_written > 0)
|
||||
debug_assert(bytes_written == (ssize_t)da.pos);
|
||||
else
|
||||
ret = (LibError)bytes_written;
|
||||
}
|
||||
|
||||
(void)da_free(&da);
|
||||
return ret;
|
||||
}
|
||||
|
@ -233,10 +233,6 @@ extern LibError tex_validate(const Tex* t);
|
||||
extern void tex_set_global_orientation(int orientation);
|
||||
|
||||
|
||||
//
|
||||
// open/close
|
||||
//
|
||||
|
||||
/**
|
||||
* manually register codecs. must be called before first use of a
|
||||
* codec (e.g. loading a texture).
|
||||
@ -249,16 +245,28 @@ extern void tex_set_global_orientation(int orientation);
|
||||
extern void tex_codec_register_all();
|
||||
|
||||
/**
|
||||
* load the specified image from file into a Tex object.
|
||||
* decode an in-memory texture file into texture object.
|
||||
*
|
||||
* FYI, currently BMP, TGA, JPG, JP2, PNG, DDS are supported - but don't
|
||||
* rely on this (not all codecs may be included).
|
||||
*
|
||||
* @param fn filename
|
||||
* @param t output texture object
|
||||
* @param data input data
|
||||
* @param data_size its size [bytes]
|
||||
* @param t output texture object.
|
||||
* @return LibError.
|
||||
**/
|
||||
extern LibError tex_decode(shared_ptr<u8> data, size_t data_size, Tex* t);
|
||||
|
||||
/**
|
||||
* encode a texture into a memory buffer in the desired file format.
|
||||
*
|
||||
* @param t input texture object
|
||||
* @param extension (including '.')
|
||||
* @param da output memory array. allocated here; caller must free it
|
||||
* when no longer needed. invalid unless function succeeds.
|
||||
* @return LibError
|
||||
**/
|
||||
extern LibError tex_load(const char* fn, Tex* t);
|
||||
extern LibError tex_encode(Tex* t, const std::string& extension, DynArray* da);
|
||||
|
||||
/**
|
||||
* store the given image data into a Tex object; this will be as if
|
||||
@ -420,14 +428,4 @@ extern bool tex_is_known_extension(const char* filename);
|
||||
**/
|
||||
extern size_t tex_hdr_size(const char* fn);
|
||||
|
||||
/**
|
||||
* write the specified texture to disk.
|
||||
*
|
||||
* @param t input texture object. note: cannot be made const because the
|
||||
* image may have to be transformed to write it out in the format
|
||||
* determined by <fn>'s extension.
|
||||
* @return LibError
|
||||
**/
|
||||
extern LibError tex_write(Tex* t, const char* fn);
|
||||
|
||||
#endif // INCLUDED_TEX
|
||||
|
@ -58,9 +58,9 @@ static bool bmp_is_hdr(const u8* file)
|
||||
}
|
||||
|
||||
|
||||
static bool bmp_is_ext(const char* ext)
|
||||
static bool bmp_is_ext(const std::string& extension)
|
||||
{
|
||||
return !strcasecmp(ext, "bmp");
|
||||
return !strcasecmp(extension.c_str(), ".bmp");
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,13 +41,12 @@ int tex_codec_register(TexCodecVTbl* c)
|
||||
// or return ERR::TEX_UNKNOWN_FORMAT if unknown.
|
||||
// note: does not raise a warning because it is used by
|
||||
// tex_is_known_extension.
|
||||
LibError tex_codec_for_filename(const char* fn, const TexCodecVTbl** c)
|
||||
LibError tex_codec_for_filename(const std::string& extension, const TexCodecVTbl** c)
|
||||
{
|
||||
const char* ext = path_extension(fn);
|
||||
for(*c = codecs; *c; *c = (*c)->next)
|
||||
{
|
||||
// we found it
|
||||
if((*c)->is_ext(ext))
|
||||
if((*c)->is_ext(extension))
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
@ -131,12 +130,11 @@ void tex_codec_register_all()
|
||||
//
|
||||
// note: we don't allocate the data param ourselves because this function is
|
||||
// needed for encoding, too (where data is already present).
|
||||
LibError tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
||||
uint src_flags, uint dst_orientation, RowArray& rows)
|
||||
shared_ptr<RowPtr> tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, uint src_flags, uint dst_orientation)
|
||||
{
|
||||
const bool flip = !tex_orientations_match(src_flags, dst_orientation);
|
||||
|
||||
rows = new RowPtr[h];
|
||||
shared_ptr<RowPtr> rows(new RowPtr[h]);
|
||||
|
||||
// determine start position and direction
|
||||
RowPtr pos = flip? data+pitch*(h-1) : data;
|
||||
@ -145,12 +143,12 @@ LibError tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
||||
|
||||
for(size_t i = 0; i < h; i++)
|
||||
{
|
||||
rows[i] = pos;
|
||||
rows.get()[i] = pos;
|
||||
pos += add;
|
||||
}
|
||||
|
||||
debug_assert(pos == end);
|
||||
return INFO::OK;
|
||||
return rows;
|
||||
}
|
||||
|
||||
|
||||
|
@ -77,11 +77,10 @@ struct TexCodecVTbl
|
||||
* caller compare it (-> smaller code) because a codec's file format
|
||||
* may have several valid extensions (e.g. jpg and jpeg).
|
||||
*
|
||||
* @param ext non-NULL extension string; does not contain '.'.
|
||||
* must be compared as case-insensitive.
|
||||
* @param extension (including '.')
|
||||
* @return bool
|
||||
**/
|
||||
bool (*is_ext)(const char* ext);
|
||||
bool (*is_ext)(const std::string& extension);
|
||||
|
||||
/**
|
||||
* return size of the file header supported by this codec.
|
||||
@ -149,14 +148,12 @@ extern int tex_codec_register(TexCodecVTbl* c);
|
||||
/**
|
||||
* find codec that recognizes the desired output file extension.
|
||||
*
|
||||
* @param fn filename; only the extension (that after '.') is used.
|
||||
* case-insensitive.
|
||||
* @param c (out) vtbl of responsible codec
|
||||
* @return LibError; ERR::RES_UNKNOWN_FORMAT (without warning, because this is
|
||||
* called by tex_is_known_extension) if no codec indicates they can
|
||||
* handle the given extension.
|
||||
**/
|
||||
extern LibError tex_codec_for_filename(const char* fn, const TexCodecVTbl** c);
|
||||
extern LibError tex_codec_for_filename(const std::string& extension, const TexCodecVTbl** c);
|
||||
|
||||
/**
|
||||
* find codec that recognizes the header's magic field.
|
||||
@ -215,9 +212,7 @@ extern LibError tex_codec_transform(Tex* t, uint transforms);
|
||||
* @return LibError
|
||||
**/
|
||||
typedef const u8* RowPtr;
|
||||
typedef RowPtr* RowArray;
|
||||
extern LibError tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
||||
uint src_flags, uint dst_orientation, RowArray& rows);
|
||||
extern shared_ptr<RowPtr> tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch, uint src_flags, uint dst_orientation);
|
||||
|
||||
/**
|
||||
* apply transforms and then copy header and image into output buffer.
|
||||
|
@ -589,9 +589,9 @@ static bool dds_is_hdr(const u8* file)
|
||||
}
|
||||
|
||||
|
||||
static bool dds_is_ext(const char* ext)
|
||||
static bool dds_is_ext(const std::string& extension)
|
||||
{
|
||||
return !strcasecmp(ext, "dds");
|
||||
return !strcasecmp(extension.c_str(), ".dds");
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,30 +41,4 @@ extern LibError tex_validate_plain_format(uint bpp, uint flags);
|
||||
**/
|
||||
extern bool tex_orientations_match(uint src_flags, uint dst_orientation);
|
||||
|
||||
|
||||
/**
|
||||
* decode an in-memory texture file into texture object.
|
||||
*
|
||||
* split out of tex_load to ease resource cleanup and allow
|
||||
* decoding images without needing to write out to disk.
|
||||
*
|
||||
* @param data input data
|
||||
* @param data_size its size [bytes]
|
||||
* @param t output texture object.
|
||||
* @return LibError.
|
||||
**/
|
||||
extern LibError tex_decode(const u8* data, size_t data_size, Tex* t);
|
||||
|
||||
/**
|
||||
* encode a texture into a memory buffer in the desired file format.
|
||||
*
|
||||
* @param t input texture object
|
||||
* @param fn filename; only used to determine the desired file format
|
||||
* (via extension)
|
||||
* @param da output memory array. allocated here; caller must free it
|
||||
* when no longer needed. invalid unless function succeeds.
|
||||
* @return LibError
|
||||
**/
|
||||
extern LibError tex_encode(Tex* t, const char* fn, DynArray* da);
|
||||
|
||||
#endif // #ifndef INCLUDED_TEX_INTERNAL
|
||||
|
@ -409,7 +409,7 @@ static LibError jpg_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
||||
// due to less copying.
|
||||
|
||||
|
||||
static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, RowArray& rows, Tex* t)
|
||||
static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, Tex* t)
|
||||
{
|
||||
src_prepare(cinfo, da);
|
||||
|
||||
@ -449,10 +449,10 @@ static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, Row
|
||||
shared_ptr<u8> data = io_Allocate(img_size);
|
||||
|
||||
// read rows
|
||||
RETURN_ERR(tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0, rows));
|
||||
shared_ptr<RowPtr> rows = tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0);
|
||||
// could use cinfo->output_scanline to keep track of progress,
|
||||
// but we need to count lines_left anyway (paranoia).
|
||||
JSAMPARRAY row = (JSAMPARRAY)rows;
|
||||
JSAMPARRAY row = (JSAMPARRAY)rows.get();
|
||||
JDIMENSION lines_left = h;
|
||||
while(lines_left != 0)
|
||||
{
|
||||
@ -467,8 +467,6 @@ static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, Row
|
||||
// mem data source.
|
||||
(void)jpeg_finish_decompress(cinfo);
|
||||
|
||||
delete[] rows;
|
||||
|
||||
LibError ret = INFO::OK;
|
||||
if(cinfo->err->num_warnings != 0)
|
||||
ret = WARN::TEX_INVALID_DATA;
|
||||
@ -486,7 +484,7 @@ static LibError jpg_decode_impl(DynArray* da, jpeg_decompress_struct* cinfo, Row
|
||||
}
|
||||
|
||||
|
||||
static LibError jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, RowArray& rows, DynArray* da)
|
||||
static LibError jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, DynArray* da)
|
||||
{
|
||||
dst_prepare(cinfo, da);
|
||||
|
||||
@ -509,11 +507,11 @@ static LibError jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, RowArray& r
|
||||
|
||||
const size_t pitch = t->w * t->bpp / 8;
|
||||
u8* data = tex_get_data(t);
|
||||
RETURN_ERR(tex_codec_alloc_rows(data, t->h, pitch, t->flags, TEX_TOP_DOWN, rows));
|
||||
shared_ptr<RowPtr> rows = tex_codec_alloc_rows(data, t->h, pitch, t->flags, TEX_TOP_DOWN);
|
||||
|
||||
// could use cinfo->output_scanline to keep track of progress,
|
||||
// but we need to count lines_left anyway (paranoia).
|
||||
JSAMPARRAY row = (JSAMPARRAY)rows;
|
||||
JSAMPARRAY row = (JSAMPARRAY)rows.get();
|
||||
JDIMENSION lines_left = t->h;
|
||||
while(lines_left != 0)
|
||||
{
|
||||
@ -526,8 +524,6 @@ static LibError jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, RowArray& r
|
||||
|
||||
jpeg_finish_compress(cinfo);
|
||||
|
||||
delete[] rows;
|
||||
|
||||
LibError ret = INFO::OK;
|
||||
if(cinfo->err->num_warnings != 0)
|
||||
ret = WARN::TEX_INVALID_DATA;
|
||||
@ -545,9 +541,9 @@ static bool jpg_is_hdr(const u8* file)
|
||||
}
|
||||
|
||||
|
||||
static bool jpg_is_ext(const char* ext)
|
||||
static bool jpg_is_ext(const std::string& extension)
|
||||
{
|
||||
return !strcasecmp(ext, "jpg") || !strcasecmp(ext, "jpeg");
|
||||
return !strcasecmp(extension.c_str(), ".jpg") || !strcasecmp(extension.c_str(), ".jpeg");
|
||||
}
|
||||
|
||||
|
||||
@ -569,10 +565,7 @@ static LibError jpg_decode(DynArray* RESTRICT da, Tex* RESTRICT t)
|
||||
|
||||
jpeg_create_decompress(&cinfo);
|
||||
|
||||
// array of pointers to scanlines (see rationale above)
|
||||
RowArray rows = 0;
|
||||
LibError ret = jpg_decode_impl(da, &cinfo, rows, t);
|
||||
free(rows);
|
||||
LibError ret = jpg_decode_impl(da, &cinfo, t);
|
||||
|
||||
jpeg_destroy_decompress(&cinfo); // releases a "good deal" of memory
|
||||
|
||||
@ -593,10 +586,7 @@ static LibError jpg_encode(Tex* RESTRICT t, DynArray* RESTRICT da)
|
||||
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
// array of pointers to scanlines (see rationale above)
|
||||
RowArray rows = 0;
|
||||
LibError ret = jpg_encode_impl(t, &cinfo, rows, da);
|
||||
free(rows);
|
||||
LibError ret = jpg_encode_impl(t, &cinfo, da);
|
||||
|
||||
jpeg_destroy_compress(&cinfo); // releases a "good deal" of memory
|
||||
|
||||
|
@ -70,7 +70,7 @@ static LibError png_transform(Tex* UNUSED(t), uint UNUSED(transforms))
|
||||
|
||||
// split out of png_decode to simplify resource cleanup and avoid
|
||||
// "dtor / setjmp interaction" warning.
|
||||
static LibError png_decode_impl(DynArray* da, png_structp png_ptr, png_infop info_ptr, RowArray& rows, Tex* t)
|
||||
static LibError png_decode_impl(DynArray* da, png_structp png_ptr, png_infop info_ptr, Tex* t)
|
||||
{
|
||||
png_set_read_fn(png_ptr, da, io_read);
|
||||
|
||||
@ -97,11 +97,9 @@ static LibError png_decode_impl(DynArray* da, png_structp png_ptr, png_infop inf
|
||||
const size_t img_size = pitch * h;
|
||||
shared_ptr<u8> data = io_Allocate(img_size);
|
||||
|
||||
RETURN_ERR(tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0, rows));
|
||||
|
||||
png_read_image(png_ptr, (png_bytepp)rows);
|
||||
shared_ptr<RowPtr> rows = tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0);
|
||||
png_read_image(png_ptr, (png_bytepp)rows.get());
|
||||
png_read_end(png_ptr, info_ptr);
|
||||
delete[] rows;
|
||||
|
||||
// success; make sure all data was consumed.
|
||||
debug_assert(da->pos == da->cur_size);
|
||||
@ -121,7 +119,7 @@ static LibError png_decode_impl(DynArray* da, png_structp png_ptr, png_infop inf
|
||||
|
||||
// split out of png_encode to simplify resource cleanup and avoid
|
||||
// "dtor / setjmp interaction" warning.
|
||||
static LibError png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, RowArray& rows, DynArray* da)
|
||||
static LibError png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, DynArray* da)
|
||||
{
|
||||
const png_uint_32 w = t->w, h = t->h;
|
||||
const size_t pitch = w * t->bpp / 8;
|
||||
@ -148,14 +146,13 @@ static LibError png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
u8* data = tex_get_data(t);
|
||||
RETURN_ERR(tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN, rows));
|
||||
shared_ptr<RowPtr> rows = tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN);
|
||||
|
||||
// PNG is native RGB.
|
||||
const int png_transforms = (t->flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;
|
||||
|
||||
png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
|
||||
png_set_rows(png_ptr, info_ptr, (png_bytepp)rows.get());
|
||||
png_write_png(png_ptr, info_ptr, png_transforms, 0);
|
||||
delete[] rows;
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
@ -170,9 +167,9 @@ static bool png_is_hdr(const u8* file)
|
||||
}
|
||||
|
||||
|
||||
static bool png_is_ext(const char* ext)
|
||||
static bool png_is_ext(const std::string& extension)
|
||||
{
|
||||
return !strcasecmp(ext, "png");
|
||||
return !strcasecmp(extension.c_str(), ".png");
|
||||
}
|
||||
|
||||
|
||||
@ -206,9 +203,7 @@ TIMER_ACCRUE(tc_png_decode);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
RowArray rows = 0;
|
||||
ret = png_decode_impl(da, png_ptr, info_ptr, rows, t);
|
||||
free(rows);
|
||||
ret = png_decode_impl(da, png_ptr, info_ptr, t);
|
||||
|
||||
fail:
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
|
||||
@ -238,9 +233,7 @@ static LibError png_encode(Tex* RESTRICT t, DynArray* RESTRICT da)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
RowArray rows = 0;
|
||||
ret = png_encode_impl(t, png_ptr, info_ptr, rows, da);
|
||||
free(rows);
|
||||
ret = png_encode_impl(t, png_ptr, info_ptr, da);
|
||||
|
||||
// shared cleanup
|
||||
fail:
|
||||
|
@ -77,9 +77,9 @@ static bool tga_is_hdr(const u8* file)
|
||||
}
|
||||
|
||||
|
||||
static bool tga_is_ext(const char* ext)
|
||||
static bool tga_is_ext(const std::string& extension)
|
||||
{
|
||||
return !strcasecmp(ext, "tga");
|
||||
return !strcasecmp(extension.c_str(), ".tga");
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,10 +2,10 @@
|
||||
#define INCLUDED_FILESYSTEM
|
||||
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/file/path.h"
|
||||
#include "lib/file/file.h"
|
||||
#include "lib/file/io/io.h"
|
||||
#include "lib/file/vfs/vfs.h"
|
||||
#include "lib/file/file_system_util.h"
|
||||
#include "lib/file/io/io.h"
|
||||
#include "lib/file/io/write_buffer.h"
|
||||
|
||||
#include "ps/CStr.h"
|
||||
|
@ -4,10 +4,13 @@
|
||||
#include "lib/posix/posix_sock.h"
|
||||
#include "lib/ogl.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/bits.h" // round_up
|
||||
#include "lib/allocators/allocators.h"
|
||||
#include "lib/sysdep/gfx.h"
|
||||
#include "lib/sysdep/snd.h"
|
||||
#include "lib/sysdep/cpu.h"
|
||||
#include "lib/tex/tex.h"
|
||||
#include "lib/file/io/io_internal.h" // BLOCK_SIZE
|
||||
|
||||
#include "ps/GameSetup/Config.h"
|
||||
#include "ps/GameSetup/GameSetup.h"
|
||||
@ -163,6 +166,34 @@ const wchar_t* ErrorString(int err)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// write the specified texture to disk.
|
||||
// note: <t> cannot be made const because the image may have to be
|
||||
// transformed to write it out in the format determined by <fn>'s extension.
|
||||
static LibError tex_write(Tex* t, const char* fn)
|
||||
{
|
||||
const std::string extension = fs::extension(fn);
|
||||
|
||||
DynArray da;
|
||||
RETURN_ERR(tex_encode(t, extension, &da));
|
||||
|
||||
// write to disk
|
||||
LibError ret = INFO::OK;
|
||||
{
|
||||
(void)da_set_size(&da, round_up(da.cur_size, BLOCK_SIZE));
|
||||
shared_ptr<u8> file(da.base, DummyDeleter<u8>());
|
||||
const ssize_t bytes_written = g_VFS->CreateFile(fn, file, da.pos);
|
||||
if(bytes_written > 0)
|
||||
debug_assert(bytes_written == (ssize_t)da.pos);
|
||||
else
|
||||
ret = (LibError)bytes_written;
|
||||
}
|
||||
|
||||
(void)da_free(&da);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static unsigned s_nextScreenshotNumber;
|
||||
|
||||
// <extension> identifies the file format that is to be written
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include "lib/bits.h" // is_pow2
|
||||
#include "lib/res/graphics/ogl_tex.h"
|
||||
#include "Renderer.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
#include "maths/MathUtil.h"
|
||||
@ -20,6 +21,7 @@
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/World.h"
|
||||
#include "ps/Player.h"
|
||||
@ -27,11 +29,6 @@
|
||||
#include "ps/ProfileViewer.h"
|
||||
#include "simulation/LOSManager.h"
|
||||
#include "simulation/TerritoryManager.h"
|
||||
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/file/io/io.h" // io_Allocate
|
||||
#include "lib/res/graphics/ogl_tex.h"
|
||||
|
||||
#include "graphics/Camera.h"
|
||||
#include "graphics/Texture.h"
|
||||
#include "graphics/LightEnv.h"
|
||||
@ -1530,8 +1527,7 @@ int CRenderer::LoadAlphaMaps()
|
||||
// load all textures and store Handle in array
|
||||
//
|
||||
Handle textures[NumAlphaMaps] = {0};
|
||||
PathPackage pp;
|
||||
(void)path_package_set_dir(&pp, "art/textures/terrain/alphamaps/special");
|
||||
VfsPath path("art/textures/terrain/alphamaps/special");
|
||||
const char* fnames[NumAlphaMaps] = {
|
||||
"blendcircle.dds",
|
||||
"blendlshape.dds",
|
||||
@ -1554,10 +1550,9 @@ int CRenderer::LoadAlphaMaps()
|
||||
uint bpp = 0;
|
||||
for(uint i=0;i<NumAlphaMaps;i++)
|
||||
{
|
||||
(void)path_package_append_file(&pp, fnames[i]);
|
||||
// note: these individual textures can be discarded afterwards;
|
||||
// we cache the composite.
|
||||
textures[i] = ogl_tex_load(pp.path);
|
||||
textures[i] = ogl_tex_load(path/fnames[i]);
|
||||
RETURN_ERR(textures[i]);
|
||||
|
||||
// quick hack: we require plain RGB(A) format, so convert to that.
|
||||
|
@ -113,14 +113,12 @@ void ScriptingHost::RunMemScript(const char* script, size_t size, const char* fi
|
||||
// globalObject defaults to 0 (in which case we use our m_GlobalObject).
|
||||
void ScriptingHost::RunScript(const CStr& filename, JSObject* globalObject)
|
||||
{
|
||||
const char* fn = filename.c_str();
|
||||
|
||||
shared_ptr<u8> buf; size_t size;
|
||||
if(g_VFS->LoadFile(fn, buf, size) != INFO::OK) // ERRTODO: translate/pass it on
|
||||
if(g_VFS->LoadFile(filename, buf, size) != INFO::OK) // ERRTODO: translate/pass it on
|
||||
throw PSERROR_Scripting_LoadFile_OpenFailed();
|
||||
|
||||
const char* script = (const char*)buf.get();
|
||||
RunMemScript(script, size, fn, 1, globalObject);
|
||||
RunMemScript(script, size, filename.c_str(), 1, globalObject);
|
||||
}
|
||||
|
||||
jsval ScriptingHost::CallFunction(const std::string & functionName, jsval * params, int numParams)
|
||||
|
@ -10,13 +10,16 @@
|
||||
|
||||
#define LOG_CATEGORY "entity"
|
||||
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
void CEntityTemplateCollection::LoadFile( const VfsPath& pathname )
|
||||
{
|
||||
// Build the entity name -> filename mapping. This is done so that
|
||||
// the entity 'x' can be in units/x.xml, structures/x.xml, etc, and
|
||||
// we don't have to search every directory for x.xml.
|
||||
|
||||
CStrW basename(Basename(pathname));
|
||||
const CStrW basename(fs::basename((const fs::path&)pathname));
|
||||
m_templateFilenames[basename] = pathname.string();
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ void CFormationCollection::LoadFile( const VfsPath& pathname )
|
||||
// the formation 'x' can be in units/x.xml, structures/x.xml, etc, and
|
||||
// we don't have to search every directory for x.xml.
|
||||
|
||||
CStrW basename(Basename(pathname));
|
||||
const CStrW basename(fs::basename((const fs::path&)pathname));
|
||||
m_templateFilenames[basename] = pathname.string();
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
void CTechnologyCollection::LoadFile( const VfsPath& pathname )
|
||||
{
|
||||
CStrW basename(Basename(pathname));
|
||||
const CStrW basename(fs::basename((const fs::path&)pathname));
|
||||
m_techFilenames[basename] = pathname.string();
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ void CMusicPlayer::Open(char* UNUSED(filename))
|
||||
for(int i = 0; i < NUM_BUFS; i++)
|
||||
{
|
||||
alGenBuffers(1, &bufs[i].al_buffer);
|
||||
bufs[i].raw_buf = mem_alloc(RAW_BUF_SIZE, 4096);
|
||||
bufs[i].raw_buf = malloc(RAW_BUF_SIZE);
|
||||
}
|
||||
|
||||
alGenSources(1, &source);
|
||||
|
@ -168,7 +168,7 @@ void JSI_Sound::ScriptingInit()
|
||||
|
||||
CStr JSI_Sound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
||||
{
|
||||
return "[object Sound: " + CStr(h_filename(m_Handle)) + "]";
|
||||
return "[object Sound: " + h_filename(m_Handle).string() + "]";
|
||||
}
|
||||
|
||||
JSBool JSI_Sound::Construct(JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval)
|
||||
|
Loading…
Reference in New Issue
Block a user