From 5fde282d9495852f63db34797fce1fd8bf5e02c2 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 22 Sep 2005 20:47:56 +0000 Subject: [PATCH] load cube maps etc. from egg files --- panda/src/doc/eggSyntax.txt | 27 ++++++++++ panda/src/egg/eggGroupNode.cxx | 29 ++++++++--- panda/src/egg/eggTexture.I | 71 ++++++++++++++++++++++++- panda/src/egg/eggTexture.cxx | 85 +++++++++++++++++++++++++++++- panda/src/egg/eggTexture.h | 18 ++++++- panda/src/egg/parser.yxx | 10 +++- panda/src/egg2pg/eggLoader.cxx | 57 ++++++++++++++++---- panda/src/putil/animInterface.cxx | 6 +-- panda/src/putil/hashFilename.I | 12 +++++ panda/src/putil/hashFilename.cxx | 87 ++++++++++++++++++++++++++++++- panda/src/putil/hashFilename.h | 5 ++ 11 files changed, 383 insertions(+), 24 deletions(-) diff --git a/panda/src/doc/eggSyntax.txt b/panda/src/doc/eggSyntax.txt index fb641e6d3a..60b932c45f 100644 --- a/panda/src/doc/eggSyntax.txt +++ b/panda/src/doc/eggSyntax.txt @@ -171,6 +171,7 @@ appear before they are referenced. wrap { repeat-definition } wrapu { repeat-definition } wrapv { repeat-definition } + wrapw { repeat-definition } This defines the behavior of the texture image outside of the normal (u,v) range 0.0 - 1.0. It is "REPEAT" to repeat the @@ -178,6 +179,32 @@ appear before they are referenced. specified independently for each axis via "wrapu" and "wrapv", or it may be specified for both simultaneously via "wrap". + Although less often used, for 3-d textures wrapw may also be + specified, and it behaves similarly to wrapu and wrapv. + + type { texture-type } + + This may be one of the following attributes: + + 1D + 2D + 3D + CUBE_MAP + + The default is "2D", which specifies a normal, 2-d texture. If + any of the other types is specified instead, a texture image of + the corresponding type is loaded. + + If 3D or CUBE_MAP is specified, then a series of texture images + must be loaded to make up the complete texture; in this case, the + texture filename is expected to include a sequence of one or more + hash mark ("#") characters, which will be filled in with the + sequence number. The first image in the sequence must be numbered + 0, and there must be no gaps in the sequence. In this case, a + separate alpha-file designation is ignored; the alpha channel, if + present, must be included in the same image with the color + channel(s). + minfilter { filter-type } magfilter { filter-type } magfilteralpha { filter-type } diff --git a/panda/src/egg/eggGroupNode.cxx b/panda/src/egg/eggGroupNode.cxx index ec58fa12e7..64b104d181 100644 --- a/panda/src/egg/eggGroupNode.cxx +++ b/panda/src/egg/eggGroupNode.cxx @@ -33,6 +33,7 @@ #include "pt_EggMaterial.h" #include "config_egg.h" +#include "hashFilename.h" #include "dSearchPath.h" #include "deg_2_rad.h" #include "dcast.h" @@ -421,6 +422,7 @@ has_absolute_pathnames() const { //////////////////////////////////////////////////////////////////// void EggGroupNode:: resolve_filenames(const DSearchPath &searchpath) { + cerr << "resolve_filenames " << get_name() << "\n"; Children::iterator ci; for (ci = _children.begin(); ci != _children.end(); @@ -428,14 +430,27 @@ resolve_filenames(const DSearchPath &searchpath) { EggNode *child = *ci; if (child->is_of_type(EggTexture::get_class_type())) { EggTexture *tex = DCAST(EggTexture, child); - Filename tex_filename = tex->get_filename(); - tex_filename.resolve_filename(searchpath); - tex->set_filename(tex_filename); + if (tex->has_hash_filename()) { + HashFilename tex_filename = tex->get_filename(); + tex_filename.resolve_filename(searchpath); + tex->set_filename(tex_filename); + + if (tex->has_alpha_filename()) { + HashFilename alpha_filename = tex->get_alpha_filename(); + alpha_filename.resolve_filename(searchpath); + tex->set_alpha_filename(alpha_filename); + } - if (tex->has_alpha_filename()) { - Filename alpha_filename = tex->get_alpha_filename(); - alpha_filename.resolve_filename(searchpath); - tex->set_alpha_filename(alpha_filename); + } else { + Filename tex_filename = tex->get_filename(); + tex_filename.resolve_filename(searchpath); + tex->set_filename(tex_filename); + + if (tex->has_alpha_filename()) { + Filename alpha_filename = tex->get_alpha_filename(); + alpha_filename.resolve_filename(searchpath); + tex->set_alpha_filename(alpha_filename); + } } } else if (child->is_of_type(EggFilenameNode::get_class_type())) { diff --git a/panda/src/egg/eggTexture.I b/panda/src/egg/eggTexture.I index e60dffb051..54c864171f 100644 --- a/panda/src/egg/eggTexture.I +++ b/panda/src/egg/eggTexture.I @@ -17,6 +17,41 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::has_hash_filename +// Access: Published +// Description: Returns true if the texture filename is expected to +// contain a hash mark standing in for a sequence +// number, or false if the filename is a literal +// filename. This will be true only if TextureType is +// TT_3d_texture or TT_cube_map. +//////////////////////////////////////////////////////////////////// +INLINE bool EggTexture:: +has_hash_filename() const { + return (_texture_type == TT_3d_texture || + _texture_type == TT_cube_map); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_texture_type +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_texture_type(TextureType texture_type) { + _texture_type = texture_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_texture_type +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::TextureType EggTexture:: +get_texture_type() const { + return _texture_type; +} + //////////////////////////////////////////////////////////////////// // Function: EggTexture::set_format // Access: Published @@ -116,7 +151,7 @@ get_wrap_v() const { // Function: EggTexture::determine_wrap_v // Access: Published // Description: Determines the appropriate wrap in the V direction. -// This is different from get_wrap_v() in that if the U +// This is different from get_wrap_v() in that if the V // wrap is unspecified, it returns the overall wrap // value. //////////////////////////////////////////////////////////////////// @@ -125,6 +160,40 @@ determine_wrap_v() const { return (_wrap_v == WM_unspecified) ? get_wrap_mode() : get_wrap_v(); } +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::set_wrap_w +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void EggTexture:: +set_wrap_w(WrapMode mode) { + _wrap_w = mode; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::get_wrap_w +// Access: Published +// Description: Returns the amount specified for W wrap. This may be +// unspecified, even if there is an overall wrap value. +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::WrapMode EggTexture:: +get_wrap_w() const { + return _wrap_w; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::determine_wrap_w +// Access: Published +// Description: Determines the appropriate wrap in the W direction. +// This is different from get_wrap_w() in that if the W +// wrap is unspecified, it returns the overall wrap +// value. +//////////////////////////////////////////////////////////////////// +INLINE EggTexture::WrapMode EggTexture:: +determine_wrap_w() const { + return (_wrap_w == WM_unspecified) ? get_wrap_mode() : get_wrap_w(); +} + //////////////////////////////////////////////////////////////////// // Function: EggTexture::set_minfilter // Access: Published diff --git a/panda/src/egg/eggTexture.cxx b/panda/src/egg/eggTexture.cxx index b3dfe223bb..6f8871a658 100644 --- a/panda/src/egg/eggTexture.cxx +++ b/panda/src/egg/eggTexture.cxx @@ -35,10 +35,12 @@ EggTexture:: EggTexture(const string &tref_name, const string &filename) : EggFilenameNode(tref_name, filename) { + _texture_type = TT_unspecified; _format = F_unspecified; _wrap_mode = WM_unspecified; _wrap_u = WM_unspecified; _wrap_v = WM_unspecified; + _wrap_w = WM_unspecified; _minfilter = FT_unspecified; _magfilter = FT_unspecified; _anisotropic_degree = 0; @@ -75,10 +77,12 @@ operator = (const EggTexture ©) { EggRenderMode::operator = (copy); EggTransform::operator = (copy); + _texture_type = copy._texture_type; _format = copy._format; _wrap_mode = copy._wrap_mode; _wrap_u = copy._wrap_u; _wrap_v = copy._wrap_v; + _wrap_w = copy._wrap_w; _minfilter = copy._minfilter; _magfilter = copy._magfilter; _anisotropic_degree = copy._anisotropic_degree; @@ -136,6 +140,11 @@ write(ostream &out, int indent_level) const { << get_alpha_file_channel() << " }\n"; } + if (get_texture_type() != TT_unspecified) { + indent(out, indent_level + 2) + << " texture_type { " << get_texture_type() << " }\n"; + } + if (get_format() != F_unspecified) { indent(out, indent_level + 2) << " format { " << get_format() << " }\n"; @@ -156,6 +165,11 @@ write(ostream &out, int indent_level) const { << " wrapv { " << get_wrap_v() << " }\n"; } + if (get_wrap_w() != WM_unspecified) { + indent(out, indent_level + 2) + << " wrapw { " << get_wrap_w() << " }\n"; + } + if (get_minfilter() != FT_unspecified) { indent(out, indent_level + 2) << " minfilter { " << get_minfilter() << " }\n"; @@ -331,10 +345,12 @@ is_equivalent_to(const EggTexture &other, int eq) const { if (eq & E_attributes) { //cout << "compared by attributes" << endl; - if (_format != other._format || + if (_texture_type != other._texture_type || + _format != other._format || _wrap_mode != other._wrap_mode || _wrap_u != other._wrap_u || _wrap_v != other._wrap_v || + _wrap_w != other._wrap_w || _minfilter != other._minfilter || _magfilter != other._magfilter || _env_type != other._env_type) { @@ -407,6 +423,9 @@ sorts_less_than(const EggTexture &other, int eq) const { } if (eq & E_attributes) { + if (_texture_type != other._texture_type) { + return (int)_texture_type < (int)other._texture_type; + } if (_format != other._format) { return (int)_format < (int)other._format; } @@ -419,6 +438,9 @@ sorts_less_than(const EggTexture &other, int eq) const { if (_wrap_v != other._wrap_v) { return (int)_wrap_v < (int)other._wrap_v; } + if (_wrap_w != other._wrap_w) { + return (int)_wrap_w < (int)other._wrap_w; + } if (_minfilter != other._minfilter) { return (int)_minfilter < (int)other._minfilter; } @@ -560,6 +582,40 @@ multitexture_over(EggTexture *other) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: EggTexture::string_texture_type +// Access: Published, Static +// Description: Returns the Texture_ype value associated with the given +// string representation, or TT_unspecified if the string +// does not match any known TextureType value. +//////////////////////////////////////////////////////////////////// +EggTexture::TextureType EggTexture:: +string_texture_type(const string &string) { + if (cmp_nocase_uh(string, "1d") == 0 || + cmp_nocase_uh(string, "1dtexture") == 0 || + cmp_nocase_uh(string, "1d_texture") == 0) { + return TT_1d_texture; + + } else if (cmp_nocase_uh(string, "2d") == 0 || + cmp_nocase_uh(string, "2dtexture") == 0 || + cmp_nocase_uh(string, "2d_texture") == 0) { + return TT_2d_texture; + + } else if (cmp_nocase_uh(string, "3d") == 0 || + cmp_nocase_uh(string, "3dtexture") == 0 || + cmp_nocase_uh(string, "3d_texture") == 0) { + return TT_2d_texture; + + } else if (cmp_nocase_uh(string, "cube") == 0 || + cmp_nocase_uh(string, "cubemap") == 0 || + cmp_nocase_uh(string, "cube_map") == 0) { + return TT_cube_map; + + } else { + return TT_unspecified; + } +} + //////////////////////////////////////////////////////////////////// // Function: EggTexture::string_format // Access: Published, Static @@ -912,6 +968,33 @@ r_min_multitexture_sort(int sort, EggTexture::MultiTextures &cycle_detector) { } +//////////////////////////////////////////////////////////////////// +// Function: TextureType output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream &operator << (ostream &out, EggTexture::TextureType texture_type) { + switch (texture_type) { + case EggTexture::TT_unspecified: + return out << "unspecified"; + + case EggTexture::TT_1d_texture: + return out << "1d"; + + case EggTexture::TT_2d_texture: + return out << "2d"; + + case EggTexture::TT_3d_texture: + return out << "3d"; + + case EggTexture::TT_cube_map: + return out << "cube-map"; + } + + nassertr(false, out); + return out << "(**invalid**)"; +} + + //////////////////////////////////////////////////////////////////// // Function: Format output operator // Description: diff --git a/panda/src/egg/eggTexture.h b/panda/src/egg/eggTexture.h index dffe0149c9..cc3ecb9c25 100644 --- a/panda/src/egg/eggTexture.h +++ b/panda/src/egg/eggTexture.h @@ -58,6 +58,10 @@ PUBLISHED: bool has_alpha_channel(int num_components) const; + enum TextureType { + TT_unspecified, TT_1d_texture, + TT_2d_texture, TT_3d_texture, TT_cube_map + }; enum Format { F_unspecified, F_rgba, F_rgbm, F_rgba12, F_rgba8, F_rgba4, F_rgba5, @@ -146,6 +150,11 @@ PUBLISHED: TG_point_sprite, }; + INLINE bool has_hash_filename() const; + + INLINE void set_texture_type(TextureType texture_type); + INLINE TextureType get_texture_type() const; + INLINE void set_format(Format format); INLINE Format get_format() const; @@ -160,6 +169,10 @@ PUBLISHED: INLINE WrapMode get_wrap_v() const; INLINE WrapMode determine_wrap_v() const; + INLINE void set_wrap_w(WrapMode mode); + INLINE WrapMode get_wrap_w() const; + INLINE WrapMode determine_wrap_w() const; + INLINE void set_minfilter(FilterType type); INLINE FilterType get_minfilter() const; @@ -234,6 +247,7 @@ PUBLISHED: bool multitexture_over(EggTexture *other); INLINE int get_multitexture_sort() const; + static TextureType string_texture_type(const string &string); static Format string_format(const string &string); static WrapMode string_wrap_mode(const string &string); static FilterType string_filter_type(const string &string); @@ -265,8 +279,9 @@ private: F_has_alpha_scale = 0x0200, }; + TextureType _texture_type; Format _format; - WrapMode _wrap_mode, _wrap_u, _wrap_v; + WrapMode _wrap_mode, _wrap_u, _wrap_v, _wrap_w; FilterType _minfilter, _magfilter; int _anisotropic_degree; EnvType _env_type; @@ -344,6 +359,7 @@ INLINE ostream &operator << (ostream &out, const EggTexture &n) { return out << n.get_filename(); } +EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::TextureType texture_type); EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::Format format); EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::WrapMode mode); EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::FilterType type); diff --git a/panda/src/egg/parser.yxx b/panda/src/egg/parser.yxx index db6bb02e6a..382395de25 100644 --- a/panda/src/egg/parser.yxx +++ b/panda/src/egg/parser.yxx @@ -344,7 +344,15 @@ texture_body: double value = $<_number>5; string strval = $<_string>5; - if (cmp_nocase_uh(name, "format") == 0) { + if (cmp_nocase_uh(name, "type") == 0) { + EggTexture::TextureType tt = EggTexture::string_texture_type(strval); + if (tt == EggTexture::TT_unspecified) { + eggyywarning("Unknown texture texture_type " + strval); + } else { + texture->set_texture_type(tt); + } + + } else if (cmp_nocase_uh(name, "format") == 0) { EggTexture::Format f = EggTexture::string_format(strval); if (f == EggTexture::F_unspecified) { eggyywarning("Unknown texture format " + strval); diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 8d82a78d62..a2ef36ef2b 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -852,16 +852,31 @@ load_texture(TextureDef &def, const EggTexture *egg_tex) { break; } - Texture *tex; - if (egg_tex->has_alpha_filename() && wanted_alpha) { - tex = TexturePool::load_texture(egg_tex->get_fullpath(), - egg_tex->get_alpha_fullpath(), - wanted_channels, - egg_tex->get_alpha_file_channel()); - } else { - tex = TexturePool::load_texture(egg_tex->get_fullpath(), - wanted_channels); + PT(Texture) tex; + switch (egg_tex->get_texture_type()) { + case EggTexture::TT_unspecified: + case EggTexture::TT_1d_texture: + case EggTexture::TT_2d_texture: + if (egg_tex->has_alpha_filename() && wanted_alpha) { + tex = TexturePool::load_texture(egg_tex->get_fullpath(), + egg_tex->get_alpha_fullpath(), + wanted_channels, + egg_tex->get_alpha_file_channel()); + } else { + tex = TexturePool::load_texture(egg_tex->get_fullpath(), + wanted_channels); + } + break; + + case EggTexture::TT_3d_texture: + tex = TexturePool::load_3d_texture(HashFilename(egg_tex->get_fullpath())); + break; + + case EggTexture::TT_cube_map: + tex = TexturePool::load_cube_map(HashFilename(egg_tex->get_fullpath())); + break; } + if (tex == (Texture *)NULL) { return false; } @@ -941,6 +956,30 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) { << (int)egg_tex->determine_wrap_v() << "\n"; } + switch (egg_tex->determine_wrap_w()) { + case EggTexture::WM_repeat: + tex->set_wrap_w(Texture::WM_repeat); + break; + + case EggTexture::WM_clamp: + if (egg_ignore_clamp) { + egg2pg_cat.warning() + << "Ignoring clamp request\n"; + tex->set_wrap_w(Texture::WM_repeat); + } else { + tex->set_wrap_w(Texture::WM_clamp); + } + break; + + case EggTexture::WM_unspecified: + break; + + default: + egg2pg_cat.warning() + << "Unexpected texture wrap flag: " + << (int)egg_tex->determine_wrap_w() << "\n"; + } + switch (egg_tex->get_minfilter()) { case EggTexture::FT_nearest: tex->set_minfilter(Texture::FT_nearest); diff --git a/panda/src/putil/animInterface.cxx b/panda/src/putil/animInterface.cxx index 766afdb87c..8d642e78cf 100644 --- a/panda/src/putil/animInterface.cxx +++ b/panda/src/putil/animInterface.cxx @@ -90,7 +90,7 @@ AnimInterface:: void AnimInterface:: play(double from, double to) { if (from >= to) { - pose(from); + pose((int)from); return; } @@ -122,7 +122,7 @@ play(double from, double to) { void AnimInterface:: loop(bool restart, double from, double to) { if (from >= to) { - pose(from); + pose((int)from); return; } @@ -157,7 +157,7 @@ loop(bool restart, double from, double to) { void AnimInterface:: pingpong(bool restart, double from, double to) { if (from >= to) { - pose(from); + pose((int)from); return; } diff --git a/panda/src/putil/hashFilename.I b/panda/src/putil/hashFilename.I index e84c5ea0cf..b01b86a53c 100644 --- a/panda/src/putil/hashFilename.I +++ b/panda/src/putil/hashFilename.I @@ -42,6 +42,18 @@ HashFilename(const HashFilename ©) : { } +//////////////////////////////////////////////////////////////////// +// Function: HashFilename::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE HashFilename:: +HashFilename(const Filename ©) : + Filename(copy) +{ + locate_hash(); +} + //////////////////////////////////////////////////////////////////// // Function: HashFilename::Copy Assignment Operator // Access: Published diff --git a/panda/src/putil/hashFilename.cxx b/panda/src/putil/hashFilename.cxx index 10c6cb5949..947fa1c680 100755 --- a/panda/src/putil/hashFilename.cxx +++ b/panda/src/putil/hashFilename.cxx @@ -46,7 +46,7 @@ get_filename_index(int index) const { // Function: HashFilename::set_hash_to_end // Access: Published // Description: Replaces the part of the filename from the beginning -// of the has sequence to the end of the filename. +// of the hash sequence to the end of the filename. //////////////////////////////////////////////////////////////////// void HashFilename:: set_hash_to_end(const string &s) { @@ -57,6 +57,91 @@ set_hash_to_end(const string &s) { locate_hash(); } +//////////////////////////////////////////////////////////////////// +// Function: HashFilename::resolve_filename +// Access: Published +// Description: Searches the given search path for the filename. If +// it is found, updates the filename to the full +// pathname found and returns true; otherwise, returns +// false. +//////////////////////////////////////////////////////////////////// +bool HashFilename:: +resolve_filename(const DSearchPath &searchpath, + const string &default_extension) { + if (!has_hash()) { + return Filename::resolve_filename(searchpath, default_extension); + } + + Filename file0 = get_filename_index(0); + if (!file0.resolve_filename(searchpath, default_extension)) { + return false; + } + + int change = file0.length() - length(); + + if (file0.length() < _hash_start || _hash_end + change < 0 || + file0.substr(_hash_end + change) != substr(_hash_end)) { + // Hmm, somehow the suffix part of the filename--everything after + // the hash sequence--was changed by the above resolve operation. + // Abandon ship. + return false; + } + + // Replace the prefix part of the filename--everything before the + // hash sequence. + _filename = file0.substr(0, _hash_start + change) + substr(_hash_start); + _hash_start += change; + _hash_end += change; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: HashFilename::find_on_searchpath +// Access: Published +// Description: Performs the reverse of the resolve_filename() +// operation: assuming that the current filename is +// fully-specified pathname (i.e. beginning with '/'), +// look on the indicated search path for a directory +// under which the file can be found. When found, +// adjust the Filename to be relative to the indicated +// directory name. +// +// Returns the index of the directory on the searchpath +// at which the file was found, or -1 if it was not +// found. +//////////////////////////////////////////////////////////////////// +int HashFilename:: +find_on_searchpath(const DSearchPath &searchpath) { + if (!has_hash()) { + return Filename::find_on_searchpath(searchpath); + } + + Filename file0 = get_filename_index(0); + int index = file0.find_on_searchpath(searchpath); + if (index == -1) { + return -1; + } + + int change = file0.length() - length(); + + if (file0.length() < _hash_start || _hash_end + change < 0 || + file0.substr(_hash_end + change) != substr(_hash_end)) { + // Hmm, somehow the suffix part of the filename--everything after + // the hash sequence--was changed by the above resolve operation. + // Abandon ship. + return false; + } + + // Replace the prefix part of the filename--everything before the + // hash sequence. + _filename = file0.substr(0, _hash_start + change) + substr(_hash_start); + _hash_start += change; + _hash_end += change; + + return index; +} + //////////////////////////////////////////////////////////////////// // Function: HashFilename::locate_hash // Access: Private diff --git a/panda/src/putil/hashFilename.h b/panda/src/putil/hashFilename.h index ef86e6df77..ea816a4f67 100644 --- a/panda/src/putil/hashFilename.h +++ b/panda/src/putil/hashFilename.h @@ -38,6 +38,7 @@ class EXPCL_PANDA HashFilename : public Filename { PUBLISHED: INLINE HashFilename(const string &filename_pattern = string()); INLINE HashFilename(const HashFilename ©); + INLINE HashFilename(const Filename ©); INLINE void operator = (const HashFilename ©); INLINE void operator = (const Filename ©); INLINE ~HashFilename(); @@ -47,6 +48,10 @@ PUBLISHED: INLINE string get_hash_to_end() const; void set_hash_to_end(const string &s); + + bool resolve_filename(const DSearchPath &searchpath, + const string &default_extension = string()); + int find_on_searchpath(const DSearchPath &searchpath); private: void locate_hash();