Status DeleteDirectory(const OsPath& path) { // note: we have to recursively empty the directory before it can // be deleted (required by Windows and POSIX rmdir()). CFileInfos files; DirectoryNames subdirectoryNames; RETURN_STATUS_IF_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames)); // delete files for(size_t i = 0; i < files.size(); i++) { const OsPath pathname = path / files[i].Name(); errno = 0; if(wunlink(pathname) != 0) WARN_RETURN(StatusFromErrno()); } // recurse over subdirectoryNames for(size_t i = 0; i < subdirectoryNames.size(); i++) RETURN_STATUS_IF_ERR(DeleteDirectory(path / subdirectoryNames[i])); errno = 0; if(wrmdir(path) != 0) WARN_RETURN(StatusFromErrno()); return INFO::OK; }
static Status load_sys_cursor(const PIVFS& vfs, const VfsPath& pathname, int hx, int hy, sys_cursor* cursor) { #if !ALLOW_SYS_CURSOR UNUSED2(vfs); UNUSED2(pathname); UNUSED2(hx); UNUSED2(hy); UNUSED2(cursor); return ERR::FAIL; #else shared_ptr<u8> file; size_t fileSize; RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize)); Tex t; RETURN_STATUS_IF_ERR(t.decode(file, fileSize)); // convert to required BGRA format. const size_t flags = (t.m_Flags | TEX_BGR) & ~TEX_DXT; RETURN_STATUS_IF_ERR(t.transform_to(flags)); void* bgra_img = t.get_data(); if(!bgra_img) WARN_RETURN(ERR::FAIL); RETURN_STATUS_IF_ERR(sys_cursor_create((int)t.m_Width, (int)t.m_Height, bgra_img, hx, hy, cursor)); return INFO::OK; #endif }
Status 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, maxSectorSize) && alignedSize != 0) { io::Operation op(*m_file.get(), (void*)data, alignedSize, m_alignedOfs); RETURN_STATUS_IF_ERR(io::Run(op)); m_alignedOfs += (off_t)alignedSize; data += alignedSize; size -= alignedSize; } const size_t chunkSize = std::min(size, BLOCK_SIZE-m_bytesUsed); memcpy(m_alignedBuf.get()+m_bytesUsed, data, chunkSize); m_bytesUsed += chunkSize; data += chunkSize; size -= chunkSize; if(m_bytesUsed == BLOCK_SIZE) RETURN_STATUS_IF_ERR(WriteBlock()); } return INFO::OK; }
Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream) { PFile file(new File); RETURN_STATUS_IF_ERR(file->Open(pathname, L'r')); shared_ptr<OggStreamImpl<VorbisFileAdapter> > tmp(new OggStreamImpl<VorbisFileAdapter>(VorbisFileAdapter(file))); RETURN_STATUS_IF_ERR(tmp->Open()); stream = tmp; return INFO::OK; }
Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream) { shared_ptr<u8> contents; size_t size; RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, contents, size)); shared_ptr<OggStreamImpl<VorbisBufferAdapter> > tmp(new OggStreamImpl<VorbisBufferAdapter>(VorbisBufferAdapter(contents, size))); RETURN_STATUS_IF_ERR(tmp->Open()); stream = tmp; return INFO::OK; }
Status Activate() { RETURN_STATUS_IF_ERR(MapRegisters(m_hpetRegisters)); RETURN_STATUS_IF_ERR(VerifyCapabilities(m_frequency, m_counterBits)); // start the counter (if not already running) Write64(CONFIG, Read64(CONFIG)|1); // note: to avoid interfering with any other users of the timer // (e.g. Vista QPC), we don't reset the counter value to 0. return INFO::OK; }
Status da_append(DynArray* da, const void* data, size_t size) { RETURN_STATUS_IF_ERR(da_reserve(da, size)); memcpy(da->base+da->pos, data, size); da->pos += size; return INFO::OK; }
Status da_reserve(DynArray* da, size_t size) { if(da->pos+size > da->cur_size_pa) RETURN_STATUS_IF_ERR(da_set_size(da, da->cur_size_pa+size)); da->cur_size = std::max(da->cur_size, da->pos+size); return INFO::OK; }
Status CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint) { if(path.empty()) return INFO::OK; struct stat s; if(wstat(path, &s) == 0) { if(!S_ISDIR(s.st_mode)) // encountered a file WARN_RETURN(ERR::FAIL); return INFO::OK; } // If we were passed a path ending with '/', strip the '/' now so that // we can consistently use Parent to find parent directory names if(path.IsDirectory()) return CreateDirectories(path.Parent(), mode, breakpoint); RETURN_STATUS_IF_ERR(CreateDirectories(path.Parent(), mode)); errno = 0; if(wmkdir(path, mode) != 0) { debug_printf("CreateDirectories: failed to mkdir %s (mode %d)\n", path.string8().c_str(), mode); if (breakpoint) WARN_RETURN(StatusFromErrno()); else return StatusFromErrno(); } return INFO::OK; }
static Handle reuse_existing_handle(uintptr_t key, H_Type type, size_t flags) { if(flags & RES_NO_CACHE) return 0; // object of specified key and type doesn't exist yet Handle h = h_find(type, key); if(h <= 0) return 0; HDATA* hd; RETURN_STATUS_IF_ERR(h_data_tag_type(h, type, hd)); // h_find means this won't fail hd->refs += 1; // we are reactivating a closed but cached handle. // need to generate a new tag so that copies of the // previous handle can no longer access the resource. // (we don't need to reset the tag in h_free, because // use before this fails due to refs > 0 check in h_user_data). if(hd->refs == 1) { const Tag tag = gen_tag(); h = handle(h_idx(h), tag); // can't fail hd->h = h; } return h; }
virtual Status GetFilePriority(const VfsPath& pathname, size_t* ppriority) const { ScopedLock s; VfsDirectory* directory; VfsFile* file; RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); *ppriority = file->Priority(); return INFO::OK; }
// retrieve the internal reference count or a negative error code. // background: since h_alloc has no way of indicating whether it // allocated a new handle or reused an existing one, counting references // within resource control blocks is impossible. since that is sometimes // necessary (always wrapping objects in Handles is excessive), we // provide access to the internal reference count. intptr_t h_get_refcnt(Handle h) { HDATA* hd; RETURN_STATUS_IF_ERR(h_data_tag(h, hd)); ENSURE(hd->refs); // if there are no refs, how did the caller manage to keep a Handle?! return hd->refs; }
Status UnalignedWriter::WriteBlock() const { io::Operation op(*m_file.get(), m_alignedBuf.get(), BLOCK_SIZE, m_alignedOfs); RETURN_STATUS_IF_ERR(io::Run(op)); m_alignedOfs += BLOCK_SIZE; m_bytesUsed = 0; return INFO::OK; }
virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */) { ScopedLock s; if(!DirectoryExists(path)) { if(flags & VFS_MOUNT_MUST_EXIST) return ERR::VFS_DIR_NOT_FOUND; // NOWARN else RETURN_STATUS_IF_ERR(CreateDirectories(path, 0700)); } VfsDirectory* directory; WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE)); PRealDirectory realDirectory(new RealDirectory(path, priority, flags)); RETURN_STATUS_IF_ERR(vfs_Attach(directory, realDirectory)); return INFO::OK; }
virtual Status GetVirtualPath(const OsPath& realPathname, VfsPath& pathname) { ScopedLock s; const OsPath realPath = realPathname.Parent()/""; VfsPath path; RETURN_STATUS_IF_ERR(FindRealPathR(realPath, m_rootDirectory, L"", path)); pathname = path / realPathname.Filename(); return INFO::OK; }
// (moved into a separate function to simplify error handling) static inline Status ActivateCounter(ICounter* counter) { RETURN_STATUS_IF_ERR(counter->Activate()); if(!counter->IsSafe()) return ERR::WHRT_COUNTER_UNSAFE; // NOWARN (happens often) return INFO::OK; }
Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames) { // open directory errno = 0; WDIR* pDir = wopendir(path); if(!pDir) return StatusFromErrno(); // NOWARN shared_ptr<WDIR> osDir(pDir, DirDeleter()); for(;;) { errno = 0; struct wdirent* osEnt = wreaddir(osDir.get()); if(!osEnt) { // no error, just no more entries to return if(!errno) return INFO::OK; WARN_RETURN(StatusFromErrno()); } for(size_t i = 0; osEnt->d_name[i] != '\0'; i++) RETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i])); const OsPath name(osEnt->d_name); // get file information (mode, size, mtime) struct stat s; #if OS_WIN // .. return wdirent directly (much faster than calling stat). RETURN_STATUS_IF_ERR(wreaddir_stat_np(osDir.get(), &s)); #else // .. call regular stat(). errno = 0; const OsPath pathname = path / name; if(wstat(pathname, &s) != 0) WARN_RETURN(StatusFromErrno()); #endif if(files && S_ISREG(s.st_mode)) files->push_back(CFileInfo(name, s.st_size, s.st_mtime)); else if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L"." && name != L"..") subdirectoryNames->push_back(name); } }
Status pool_create(Pool* p, size_t max_size, size_t el_size) { if(el_size == POOL_VARIABLE_ALLOCS) p->el_size = 0; else p->el_size = Align<allocationAlignment>(el_size); p->freelist = mem_freelist_Sentinel(); RETURN_STATUS_IF_ERR(da_alloc(&p->da, max_size)); return INFO::OK; }
virtual Status RepopulateDirectory(const VfsPath& path) { ScopedLock s; VfsDirectory* directory; RETURN_STATUS_IF_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0)); directory->RequestRepopulate(); return INFO::OK; }
// get HDATA for the given handle. // only uses (and checks) the index field. // used by h_force_close (which must work regardless of tag). static inline Status h_data_no_tag(const Handle h, HDATA*& hd) { ssize_t idx = (ssize_t)h_idx(h); RETURN_STATUS_IF_ERR(h_data_from_idx(idx, hd)); // need to verify it's in range - h_data_from_idx can only verify that // it's < maximum allowable index. if(uintptr_t(hd) > uintptr_t(hpool.da.base)+hpool.da.pos) WARN_RETURN(ERR::H_IDX_UNUSED); return INFO::OK; }
virtual Status RemoveFile(const VfsPath& pathname) { ScopedLock s; m_fileCache.Remove(pathname); VfsDirectory* directory; VfsFile* file; RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); directory->RemoveFile(file->Name()); return INFO::OK; }
// any further params are passed to type's init routine Handle h_alloc(H_Type type, const PIVFS& vfs, const VfsPath& pathname, size_t flags, ...) { H_ScopedLock s; RETURN_STATUS_IF_ERR(type_validate(type)); const uintptr_t key = fnv_hash(pathname.string().c_str(), pathname.string().length()*sizeof(pathname.string()[0])); // see if we can reuse an existing handle Handle h = reuse_existing_handle(key, type, flags); RETURN_STATUS_IF_ERR(h); // .. successfully reused the handle; refcount increased if(h > 0) return h; // .. need to allocate a new one: va_list args; va_start(args, flags); h = alloc_new_handle(type, vfs, pathname, key, flags, &args); va_end(args); return h; // alloc_new_handle already does WARN_RETURN_STATUS_IF_ERR }
Status sys_cursor_create(int w, int h, void* bgra_img, int hx, int hy, sys_cursor* cursor) { // alpha-blended cursors do not work on a 16-bit display // (they get drawn as a black square), so refuse to load the // cursor in that case int bpp = 0; RETURN_STATUS_IF_ERR(gfx::GetVideoMode(NULL, NULL, &bpp, NULL)); if (bpp <= 16) return ERR::FAIL; return sys_cursor_create_common(w, h, bgra_img, NULL, hx, hy, cursor); }
static Status InitStructures() { #if OS_WIN wfirmware::Table table; RETURN_STATUS_IF_ERR(GetTable(table)); #else std::vector<u8> table; return ERR::NOT_SUPPORTED; #endif // (instead of counting the total string size, just use the // SMBIOS size - typically 1-2 KB - as an upper bound.) stringStoragePos = stringStorage = (char*)calloc(table.size(), sizeof(char)); // freed in Cleanup if(!stringStorage) WARN_RETURN(ERR::NO_MEM); atexit(Cleanup); const Header* header = (const Header*)&table[0]; const Header* const end = (const Header*)(&table[0] + table.size()); for(;;) { if(header+1 > end) { debug_printf("SMBIOS: table not terminated\n"); break; } if(header->id == 127) // end break; if(header->length < sizeof(Header)) return ERR::_3; // NOWARN (happens on some unknown BIOS, see http://trac.wildfiregames.com/ticket/2985) const Header* next; const Strings strings = ExtractStrings(header, (const char*)end, next); switch(header->id) { #define STRUCTURE(name, id) case id: AddStructure(header, strings, structures.name##_); break; STRUCTURES #undef STRUCTURE default: if(32 < header->id && header->id < 126) // only mention non-proprietary structures of which we are not aware debug_printf("SMBIOS: unknown structure type %d\n", header->id); break; } header = next; } return INFO::OK; }
// get HDATA for the given handle. // also verifies the type. // used by most functions accessing handle data. static Status h_data_tag_type(const Handle h, const H_Type type, HDATA*& hd) { RETURN_STATUS_IF_ERR(h_data_tag(h, hd)); // h_alloc makes sure type isn't 0, so no need to check that here. if(hd->type != type) { debug_printf(L"h_mgr: expected type %ls, got %ls\n", hd->type->name, type->name); WARN_RETURN(ERR::H_TYPE_MISMATCH); } return INFO::OK; }
// force the resource to be freed immediately, even if cached. // tag is not checked - this allows the first Handle returned // (whose tag will change after being 'freed', but remaining in memory) // to later close the object. // this is used when reinitializing the sound engine - // at that point, all (cached) OpenAL resources must be freed. Status h_force_free(Handle h, H_Type type) { H_ScopedLock s; // require valid index; ignore tag; type checked below. HDATA* hd; RETURN_STATUS_IF_ERR(h_data_no_tag(h, hd)); if(hd->type != type) WARN_RETURN(ERR::H_TYPE_MISMATCH); hd->keep_open = 0; hd->refs = 0; h_free_hd(hd); return INFO::OK; }
virtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size) { ScopedLock s; const bool isCacheHit = m_fileCache.Retrieve(pathname, fileContents, size); if(!isCacheHit) { VfsDirectory* directory; VfsFile* file; // per 2010-05-01 meeting, this shouldn't raise 'scary error // dialogs', which might fail to display the culprit pathname // instead, callers should log the error, including pathname. RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); fileContents = DummySharedPtr((u8*)0); size = file->Size(); if(size != 0) // (the file cache can't handle zero-length allocations) { if(size < m_cacheSize/2) // (avoid evicting lots of previous data) fileContents = m_fileCache.Reserve(size); if(fileContents) { RETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size())); m_fileCache.Add(pathname, fileContents, size); } else { RETURN_STATUS_IF_ERR(AllocateAligned(fileContents, size, maxSectorSize)); RETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size())); } } } stats_io_user_request(size); stats_cache(isCacheHit? CR_HIT : CR_MISS, size); m_trace->NotifyLoad(pathname, size); return INFO::OK; }
Status ForEachFile(const PIVFS& fs, const VfsPath& startPath, FileCallback cb, uintptr_t cbData, const wchar_t* pattern, size_t flags) { // (declare here to avoid reallocations) CFileInfos files; DirectoryNames subdirectoryNames; // (a FIFO queue is more efficient than recursion because it uses less // stack space and avoids seeks due to breadth-first traversal.) std::queue<VfsPath> pendingDirectories; pendingDirectories.push(startPath/""); while(!pendingDirectories.empty()) { const VfsPath& path = pendingDirectories.front(); RETURN_STATUS_IF_ERR(fs->GetDirectoryEntries(path, &files, &subdirectoryNames)); for(size_t i = 0; i < files.size(); i++) { const CFileInfo fileInfo = files[i]; if(!match_wildcard(fileInfo.Name().string().c_str(), pattern)) continue; const VfsPath pathname(path / fileInfo.Name()); // (CFileInfo only stores the name) RETURN_STATUS_IF_ERR(cb(pathname, fileInfo, cbData)); } if(!(flags & DIR_RECURSIVE)) break; for(size_t i = 0; i < subdirectoryNames.size(); i++) pendingDirectories.push(path / subdirectoryNames[i]/""); pendingDirectories.pop(); } return INFO::OK; }
Status ReloadChangedFiles() { std::vector<DirWatchNotification> notifications; RETURN_STATUS_IF_ERR(dir_watch_Poll(notifications)); for(size_t i = 0; i < notifications.size(); i++) { if(!CanIgnore(notifications[i])) { VfsPath pathname; RETURN_STATUS_IF_ERR(g_VFS->GetVirtualPath(notifications[i].Pathname(), pathname)); RETURN_STATUS_IF_ERR(g_VFS->Invalidate(pathname)); // Tell each hotloadable system about this file change: RETURN_STATUS_IF_ERR(g_GUI->ReloadChangedFiles(pathname)); for (size_t j = 0; j < g_ReloadFuncs.size(); ++j) g_ReloadFuncs[j].first(g_ReloadFuncs[j].second, pathname); RETURN_STATUS_IF_ERR(h_reload(g_VFS, pathname)); } } return INFO::OK; }
Status create(const PIVFS& vfs, const VfsPath& pathname, int hotspotx_, int hotspoty_) { ht = ogl_tex_load(vfs, pathname); RETURN_STATUS_IF_ERR(ht); size_t width, height; (void)ogl_tex_get_size(ht, &width, &height, 0); w = (GLint)width; h = (GLint)height; hotspotx = hotspotx_; hotspoty = hotspoty_; (void)ogl_tex_set_filter(ht, GL_NEAREST); (void)ogl_tex_upload(ht); return INFO::OK; }