1
0
forked from 0ad/0ad

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:
janwas 2007-12-22 18:15:52 +00:00
parent e2d2789c1b
commit 8667ea74c8
64 changed files with 779 additions and 1881 deletions

View File

@ -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();

View File

@ -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();
}

View File

@ -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

View File

@ -2,6 +2,7 @@
#include "real_directory.h"
#include "lib/path_util.h"
#include "lib/file/file.h"
#include "lib/file/io/io.h"

View File

@ -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
View 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
View 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

View 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;

View File

@ -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);
};

View File

@ -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;

View File

@ -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();
}

View File

@ -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

View 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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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);
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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

View File

@ -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.

View File

@ -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);
}

View File

@ -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.

View File

@ -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)
{

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
};
/**

View File

@ -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");
}
};

View File

@ -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;
}

View File

@ -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

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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");
}

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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");
}

View File

@ -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"

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);

View File

@ -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)