diff options
author | 2018-01-10 18:25:26 +1100 | |
---|---|---|
committer | 2018-01-10 18:25:26 +1100 | |
commit | d697e8a992268228243278cc2b5a8f46b07a9208 (patch) | |
tree | 4c09eba64058c7d0cc3a78cb7b7b4c6278a3e9a2 | |
parent | 9ee066992dd0126b7ed7450d664bf055d527ab64 (diff) |
Convert zippath directory to a C++ interface
-rw-r--r-- | src/frontend/mame/ui/filesel.cpp | 16 | ||||
-rw-r--r-- | src/frontend/mame/ui/imgcntrl.cpp | 3 | ||||
-rw-r--r-- | src/lib/util/zippath.cpp | 1252 | ||||
-rw-r--r-- | src/lib/util/zippath.h | 42 | ||||
-rw-r--r-- | src/osd/modules/debugger/debugimgui.cpp | 22 |
5 files changed, 577 insertions, 758 deletions
diff --git a/src/frontend/mame/ui/filesel.cpp b/src/frontend/mame/ui/filesel.cpp index c64dcc5001e..348ed3207f4 100644 --- a/src/frontend/mame/ui/filesel.cpp +++ b/src/frontend/mame/ui/filesel.cpp @@ -296,15 +296,15 @@ void menu_file_selector::populate(float &customtop, float &custombottom) m_entrylist.clear(); // open the directory - util::zippath_directory *directory = nullptr; - osd_file::error const err = util::zippath_opendir(m_current_directory, &directory); + util::zippath_directory::ptr directory; + osd_file::error const err = util::zippath_directory::open(m_current_directory, directory); // add the "[empty slot]" entry if available if (m_has_empty) append_entry(SELECTOR_ENTRY_TYPE_EMPTY, "", ""); // add the "[create]" entry - if (m_has_create && !util::zippath_is_zip(directory)) + if (m_has_create && !directory->is_archive()) append_entry(SELECTOR_ENTRY_TYPE_CREATE, "", ""); // add and select the "[software list]" entry if available @@ -326,7 +326,7 @@ void menu_file_selector::populate(float &customtop, float &custombottom) } else { - for (osd::directory::entry const *dirent = util::zippath_readdir(directory); dirent; dirent = util::zippath_readdir(directory)) + for (osd::directory::entry const *dirent = directory->readdir(); dirent; dirent = directory->readdir()) { // append a dirent entry file_selector_entry const *entry = append_dirent_entry(dirent); @@ -342,8 +342,7 @@ void menu_file_selector::populate(float &customtop, float &custombottom) } } } - if (directory) - util::zippath_closedir(directory); + directory.reset(); // sort the menu entries const std::collate<wchar_t> &coll = std::use_facet<std::collate<wchar_t>>(std::locale()); @@ -410,7 +409,10 @@ void menu_file_selector::handle() case SELECTOR_ENTRY_TYPE_DRIVE: case SELECTOR_ENTRY_TYPE_DIRECTORY: // drive/directory - first check the path - err = util::zippath_opendir(entry->fullpath, nullptr); + { + util::zippath_directory::ptr dir; + err = util::zippath_directory::open(entry->fullpath, dir); + } if (err != osd_file::error::NONE) { // this path is problematic; present the user with an error and bail diff --git a/src/frontend/mame/ui/imgcntrl.cpp b/src/frontend/mame/ui/imgcntrl.cpp index 18ee2358852..ab502a39834 100644 --- a/src/frontend/mame/ui/imgcntrl.cpp +++ b/src/frontend/mame/ui/imgcntrl.cpp @@ -69,7 +69,8 @@ menu_control_device_image::menu_control_device_image(mame_ui_manager &mui, rende } // check to see if the path exists; if not then set to current directory - if (util::zippath_opendir(m_current_directory, nullptr) != osd_file::error::NONE) + util::zippath_directory::ptr dir; + if (util::zippath_directory::open(m_current_directory, dir) != osd_file::error::NONE) osd_get_full_path(m_current_directory, "."); } } diff --git a/src/lib/util/zippath.cpp b/src/lib/util/zippath.cpp index f85e5c63b71..e04281cd98e 100644 --- a/src/lib/util/zippath.cpp +++ b/src/lib/util/zippath.cpp @@ -22,63 +22,8 @@ namespace util { -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ - -/** - * @class zippath_directory - * - * @brief A zippath directory. - */ - -class zippath_directory -{ -public: - zippath_directory() - : returned_parent(false) - , directory() - , called_zip_first(false) - , zipfile() - , returned_dirlist() - { - } - - /* common */ - /** @brief true to returned parent. */ - bool returned_parent; - /** @brief The returned entry. */ - osd::directory::entry returned_entry; - - /* specific to normal directories */ - /** @brief Pathname of the directory. */ - osd::directory::ptr directory; - - /* specific to ZIP directories */ - /** @brief true to called zip first. */ - bool called_zip_first; - /** @brief The zipfile. */ - archive_file::ptr zipfile; - /** @brief The zipprefix. */ - std::string zipprefix; - /** @brief The returned dirlist. */ - std::forward_list<std::string> returned_dirlist; -}; - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - -static int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd::directory::entry::entry_type &type); -static bool is_zip_file(std::string const &path); -static bool is_zip_file_separator(char c); -static bool is_7z_file(std::string const &path); - - -/*************************************************************************** - PATH OPERATIONS -***************************************************************************/ +namespace { /** * @fn int is_path_separator(char c) @@ -94,424 +39,25 @@ static bool is_7z_file(std::string const &path); int is_path_separator(char c) { + // FIXME: don't assume DOS-like behaviour - backslashes are valid filename characters on *NIX return (c == '/') || (c == '\\'); } // ------------------------------------------------- -// parse_parent_path - parses out the parent path -// ------------------------------------------------- - -/** - * @fn static void parse_parent_path(const std::string &path, std::string::size_type *beginpos, std::string::size_type *endpos) - * - * @brief Parse parent path. - * - * @param path Full pathname of the file. - * @param [in,out] beginpos If non-null, the beginpos. - * @param [in,out] endpos If non-null, the endpos. - */ - -static void parse_parent_path(const std::string &path, std::string::size_type *beginpos, std::string::size_type *endpos) -{ - std::string::size_type length = path.length(); - std::string::size_type pos; - - // skip over trailing path separators - pos = length ? (length - 1) : std::string::npos; - while ((pos > 0) && (pos != std::string::npos) && is_path_separator(path[pos])) - pos--; - - // return endpos - if (endpos != nullptr) - *endpos = pos; - - // now skip until we find a path separator - while ((pos != std::string::npos) && !is_path_separator(path[pos])) - pos = (pos > 0) ? pos - 1 : std::string::npos; - - // return beginpos - if (beginpos != nullptr) - *beginpos = pos; -} - - - -// ------------------------------------------------- -// zippath_parent - retrieves the parent directory -// ------------------------------------------------- - -std::string &zippath_parent(std::string &dst, const std::string &path) -{ - std::string::size_type pos; - parse_parent_path(path, &pos, nullptr); - - if (pos != std::string::npos) - dst = path.substr(0, pos + 1); - else - dst.clear(); - return dst; -} - - - -// ------------------------------------------------- -// zippath_parent - retrieves the parent directory -// ------------------------------------------------- - -std::string zippath_parent(const std::string &path) -{ - std::string result; - zippath_parent(result, path); - return result; -} - - - -// ------------------------------------------------- -// zippath_parent_basename - retrieves the parent -// directory basename -// ------------------------------------------------- - -/** - * @fn std::string &zippath_parent_basename(std::string &dst, const std::string &path) - * - * @brief Zippath parent basename. - * - * @param [in,out] dst Destination for the. - * @param path Full pathname of the file. - * - * @return A std::string& - */ - -std::string &zippath_parent_basename(std::string &dst, const std::string &path) -{ - std::string::size_type beginpos, endpos; - parse_parent_path(path, &beginpos, &endpos); - dst.copy((char*)(path.c_str() + beginpos + 1), endpos - beginpos); - return dst; -} - - - -// ------------------------------------------------- -// zippath_parent_basename - retrieves the parent -// directory basename -// ------------------------------------------------- - -std::string zippath_parent_basename(const std::string &path) -{ - std::string result; - zippath_parent_basename(result, path); - return result; -} - - - -// ------------------------------------------------- -// zippath_combine - combines two paths -// ------------------------------------------------- - -/** - * @fn std::string &zippath_combine(std::string &dst, const char *path1, const char *path2) - * - * @brief Zippath combine. - * - * @param [in,out] dst Destination for the. - * @param path1 The first path. - * @param path2 The second path. - * - * @return A std::string& - */ - -std::string &zippath_combine(std::string &dst, const std::string &path1, const std::string &path2) -{ - if (path2 == ".") - { - dst.assign(path1); - } - else if (path2 == "..") - { - dst = zippath_parent(path1); - } - else if (osd_is_absolute_path(path2)) - { - dst.assign(path2); - } - else if (!path1.empty() && !is_path_separator(*path1.rbegin())) - { - dst.assign(path1).append(PATH_SEPARATOR).append(path2); - } - else - { - dst.assign(path1).append(path2); - } - return dst; -} - - - -// ------------------------------------------------- -// zippath_combine - combines two paths -// ------------------------------------------------- - -std::string zippath_combine(const std::string &path1, const std::string &path2) -{ - std::string result; - zippath_combine(result, path1, path2); - return result; -} - - - -/*************************************************************************** - FILE OPERATIONS -***************************************************************************/ - -// ------------------------------------------------- -// file_error_from_zip_error - translates a -// osd_file::error to a zip_error -// ------------------------------------------------- - -/** - * @fn static osd_file::error file_error_from_zip_error(archive_file::error ziperr) - * - * @brief File error from zip error. - * - * @param ziperr The ziperr. - * - * @return A osd_file::error. - */ - -static osd_file::error file_error_from_zip_error(archive_file::error ziperr) -{ - osd_file::error filerr; - switch(ziperr) - { - case archive_file::error::NONE: - filerr = osd_file::error::NONE; - break; - case archive_file::error::OUT_OF_MEMORY: - filerr = osd_file::error::OUT_OF_MEMORY; - break; - case archive_file::error::BAD_SIGNATURE: - case archive_file::error::DECOMPRESS_ERROR: - case archive_file::error::FILE_TRUNCATED: - case archive_file::error::FILE_CORRUPT: - case archive_file::error::UNSUPPORTED: - case archive_file::error::FILE_ERROR: - filerr = osd_file::error::INVALID_DATA; - break; - case archive_file::error::BUFFER_TOO_SMALL: - default: - filerr = osd_file::error::FAILURE; - break; - } - return filerr; -} - - -// ------------------------------------------------- -// create_core_file_from_zip - creates a core_file -// from a zip file entry -// ------------------------------------------------- - -/** - * @fn static osd_file::error create_core_file_from_zip(archive_file *zip, util::core_file::ptr &file) - * - * @brief Creates core file from zip. - * - * @param [in,out] zip If non-null, the zip. - * @param header The header. - * @param [in,out] file [in,out] If non-null, the file. - * - * @return The new core file from zip. - */ - -static osd_file::error create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file) -{ - osd_file::error filerr; - archive_file::error ziperr; - void *ptr; - - ptr = malloc(zip.current_uncompressed_length()); - if (ptr == nullptr) - { - filerr = osd_file::error::OUT_OF_MEMORY; - goto done; - } - - ziperr = zip.decompress(ptr, zip.current_uncompressed_length()); - if (ziperr != archive_file::error::NONE) - { - filerr = file_error_from_zip_error(ziperr); - goto done; - } - - filerr = util::core_file::open_ram_copy(ptr, zip.current_uncompressed_length(), OPEN_FLAG_READ, file); - if (filerr != osd_file::error::NONE) - goto done; - -done: - if (ptr != nullptr) - free(ptr); - return filerr; -} - - -// ------------------------------------------------- -// zippath_fopen - opens a zip path file -// ------------------------------------------------- - -/** - * @fn osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path) - * - * @brief Zippath fopen. - * - * @param filename Filename of the file. - * @param openflags The openflags. - * @param [in,out] file [in,out] If non-null, the file. - * @param [in,out] revised_path Full pathname of the revised file. - * - * @return A osd_file::error. - */ - -osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path) -{ - osd_file::error filerr = osd_file::error::NOT_FOUND; - archive_file::error ziperr; - archive_file::ptr zip; - int header; - osd::directory::entry::entry_type entry_type; - int len; - - /* first, set up the two types of paths */ - std::string mainpath(filename); - std::string subpath; - file = nullptr; - - /* loop through */ - while((file == nullptr) && (mainpath.length() > 0) - && ((openflags == OPEN_FLAG_READ) || (subpath.length() == 0))) - { - /* is the mainpath a ZIP path? */ - if (is_zip_file(mainpath) || is_7z_file(mainpath)) - { - /* this file might be a zip file - lets take a look */ - ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip); - if (ziperr == archive_file::error::NONE) - { - /* it is a zip file - error if we're not opening for reading */ - if (openflags != OPEN_FLAG_READ) - { - filerr = osd_file::error::ACCESS_DENIED; - goto done; - } - - if (subpath.length() > 0) - header = zippath_find_sub_path(*zip, subpath, entry_type); - else - header = zip->first_file(); - - if (header < 0) - { - filerr = osd_file::error::NOT_FOUND; - goto done; - } - - /* attempt to read the file */ - filerr = create_core_file_from_zip(*zip, file); - if (filerr != osd_file::error::NONE) - goto done; - - /* update subpath, if appropriate */ - if (subpath.length() == 0) - subpath.assign(zip->current_name()); - - /* we're done */ - goto done; - } - } - - if (subpath.length() == 0) - filerr = util::core_file::open(filename, openflags, file); - else - filerr = osd_file::error::NOT_FOUND; - - /* if we errored, then go up a directory */ - if (filerr != osd_file::error::NONE) - { - /* go up a directory */ - auto temp = zippath_parent(mainpath); - - /* append to the sub path */ - if (subpath.length() > 0) - { - std::string temp2; - mainpath = mainpath.substr(temp.length()); - temp2.assign(mainpath).append(PATH_SEPARATOR).append(subpath); - subpath.assign(temp2); - } - else - { - mainpath = mainpath.substr(temp.length()); - subpath.assign(mainpath); - } - /* get the new main path, truncating path separators */ - len = temp.length(); - while (len > 0 && is_zip_file_separator(temp[len - 1])) - len--; - temp = temp.substr(0, len); - mainpath.assign(temp); - } - } - -done: - /* store the revised path */ - revised_path.clear(); - if (filerr == osd_file::error::NONE) - { - /* cannonicalize mainpath */ - std::string alloc_fullpath; - filerr = osd_get_full_path(alloc_fullpath, mainpath); - if (filerr == osd_file::error::NONE) - { - revised_path = alloc_fullpath; - if (subpath.length() > 0) - revised_path.append(PATH_SEPARATOR).append(subpath); - } - } - - return filerr; -} - - -/*************************************************************************** - DIRECTORY OPERATIONS -***************************************************************************/ - -// ------------------------------------------------- // is_root - tests to see if this path is the root // ------------------------------------------------- -/** - * @fn static int is_root(const char *path) - * - * @brief Is root. - * - * @param path Full pathname of the file. - * - * @return An int. - */ - -static int is_root(const char *path) +bool is_root(std::string const &path) { - int i = 0; + // FIXME: get rid of the assumption that paths are DOS-like + + std::string::size_type i = 0; - /* skip drive letter */ + // skip drive letter if (isalpha(path[i]) && (path[i + 1] == ':')) i += 2; - /* skip path separators */ + // skip path separators while (is_path_separator(path[i])) i++; @@ -525,17 +71,7 @@ static int is_root(const char *path) // 7-zip file // ------------------------------------------------- -/** - * @fn static int is_7z_file(const char *path) - * - * @brief Is 7z file. - * - * @param path Full pathname of the file. - * - * @return An int. - */ - -static bool is_7z_file(std::string const &path) +bool is_7z_file(std::string const &path) { auto const s = path.rfind('.'); return (std::string::npos != s) && !core_stricmp(path.c_str() + s, ".7z"); @@ -547,7 +83,7 @@ static bool is_7z_file(std::string const &path) // ZIP file // ------------------------------------------------- -static bool is_zip_file(std::string const &path) +bool is_zip_file(std::string const &path) { auto const s = path.rfind('.'); return (std::string::npos != s) && !core_stricmp(path.c_str() + s, ".zip"); @@ -560,17 +96,7 @@ static bool is_zip_file(std::string const &path) // character is a path separator within a ZIP file // ------------------------------------------------- -/** - * @fn static int is_zip_file_separator(char c) - * - * @brief Is zip file separator. - * - * @param c The character. - * - * @return An int. - */ - -static bool is_zip_file_separator(char c) +bool is_zip_file_separator(char c) { return (c == '/') || (c == '\\'); } @@ -582,17 +108,7 @@ static bool is_zip_file_separator(char c) // character is a path separator within a ZIP path // ------------------------------------------------- -/** - * @fn static int is_zip_path_separator(char c) - * - * @brief Is zip path separator. - * - * @param c The character. - * - * @return An int. - */ - -static bool is_zip_path_separator(char c) +bool is_zip_path_separator(char c) { return is_zip_file_separator(c) || is_path_separator(c); } @@ -604,18 +120,7 @@ static bool is_zip_path_separator(char c) // character, normalizing separators as '/' // ------------------------------------------------- -/** - * @fn static char next_path_char(const char *s, int *pos) - * - * @brief Next path character. - * - * @param s The const char * to process. - * @param [in,out] pos If non-null, the position. - * - * @return A char. - */ - -static char next_path_char(std::string const &s, std::string::size_type &pos) +char next_path_char(std::string const &s, std::string::size_type &pos) { // skip over any initial separators if (pos == 0) @@ -654,19 +159,7 @@ static char next_path_char(std::string const &s, std::string::size_type &pos) // type of a sub path in a zip file // ------------------------------------------------- -/** - * @fn static const zip_file_header *zippath_find_sub_path(archive_file *zipfile, const char *subpath, osd::directory::entry::entry_type *type) - * - * @brief Zippath find sub path. - * - * @param [in,out] zipfile If non-null, the zipfile. - * @param subpath The subpath. - * @param [in,out] type If non-null, the type. - * - * @return null if it fails, else a zip_file_header*. - */ - -static int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd::directory::entry::entry_type &type) +int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd::directory::entry::entry_type &type) { for (int header = zipfile.first_file(); header >= 0; header = zipfile.next_file()) { @@ -701,24 +194,39 @@ static int zippath_find_sub_path(archive_file &zipfile, std::string const &subpa // ------------------------------------------------- +// parse_parent_path - parses out the parent path +// ------------------------------------------------- + +void parse_parent_path(const std::string &path, std::string::size_type *beginpos, std::string::size_type *endpos) +{ + std::string::size_type length = path.length(); + std::string::size_type pos; + + // skip over trailing path separators + pos = length ? (length - 1) : std::string::npos; + while ((pos > 0) && (pos != std::string::npos) && is_path_separator(path[pos])) + pos--; + + // return endpos + if (endpos != nullptr) + *endpos = pos; + + // now skip until we find a path separator + while ((pos != std::string::npos) && !is_path_separator(path[pos])) + pos = (pos > 0) ? pos - 1 : std::string::npos; + + // return beginpos + if (beginpos != nullptr) + *beginpos = pos; +} + + +// ------------------------------------------------- // zippath_resolve - separates a ZIP path out into // true path and ZIP entry components // ------------------------------------------------- -/** - * @fn static osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_type &entry_type, archive_file *&zipfile, std::string &newpath) - * - * @brief Zippath resolve. - * - * @param path Full pathname of the file. - * @param [in,out] entry_type Type of the entry. - * @param [in,out] zipfile [in,out] If non-null, the zipfile. - * @param [in,out] newpath The newpath. - * - * @return A osd_file::error. - */ - -static osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath) +osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath) { newpath.clear(); @@ -793,278 +301,584 @@ static osd_file::error zippath_resolve(const char *path, osd::directory::entry:: // ------------------------------------------------- -// zippath_opendir - opens a directory +// file_error_from_zip_error - translates a +// osd_file::error to a zip_error // ------------------------------------------------- -/** - * @fn osd_file::error zippath_opendir(const std::string &path, zippath_directory **directory) - * - * @brief Zippath opendir. - * - * @param path Full pathname of the file. - * @param [in,out] directory If non-null, pathname of the directory. - * - * @return A osd_file::error. - */ +osd_file::error file_error_from_zip_error(archive_file::error ziperr) +{ + osd_file::error filerr; + switch(ziperr) + { + case archive_file::error::NONE: + filerr = osd_file::error::NONE; + break; + case archive_file::error::OUT_OF_MEMORY: + filerr = osd_file::error::OUT_OF_MEMORY; + break; + case archive_file::error::BAD_SIGNATURE: + case archive_file::error::DECOMPRESS_ERROR: + case archive_file::error::FILE_TRUNCATED: + case archive_file::error::FILE_CORRUPT: + case archive_file::error::UNSUPPORTED: + case archive_file::error::FILE_ERROR: + filerr = osd_file::error::INVALID_DATA; + break; + case archive_file::error::BUFFER_TOO_SMALL: + default: + filerr = osd_file::error::FAILURE; + break; + } + return filerr; +} + -osd_file::error zippath_opendir(const std::string &path, zippath_directory **directory) +// ------------------------------------------------- +// create_core_file_from_zip - creates a core_file +// from a zip file entry +// ------------------------------------------------- + +osd_file::error create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file) { - osd_file::error err; + osd_file::error filerr; + archive_file::error ziperr; - /* allocate a directory */ - zippath_directory *result = nullptr; - try + void *ptr = malloc(zip.current_uncompressed_length()); + if (!ptr) { - result = new zippath_directory; + filerr = osd_file::error::OUT_OF_MEMORY; + goto done; } - catch (std::bad_alloc &) + + ziperr = zip.decompress(ptr, zip.current_uncompressed_length()); + if (ziperr != archive_file::error::NONE) { - err = osd_file::error::OUT_OF_MEMORY; + filerr = file_error_from_zip_error(ziperr); goto done; } - /* resolve the path */ - osd::directory::entry::entry_type entry_type; - err = zippath_resolve(path.c_str(), entry_type, result->zipfile, result->zipprefix); - if (err != osd_file::error::NONE) + + filerr = util::core_file::open_ram_copy(ptr, zip.current_uncompressed_length(), OPEN_FLAG_READ, file); + if (filerr != osd_file::error::NONE) goto done; - /* we have to be a directory */ - if (entry_type != osd::directory::entry::entry_type::DIR) +done: + if (ptr != nullptr) + free(ptr); + return filerr; +} + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +class zippath_directory_base : public zippath_directory +{ +protected: + zippath_directory_base(bool fake_parent) : m_returned_parent(!fake_parent) { - err = osd_file::error::NOT_FOUND; - goto done; } - /* was the result a ZIP? */ - if (!result->zipfile) + virtual osd::directory::entry const *readdir() override { - /* a conventional directory */ - result->directory = osd::directory::open(path); - if (!result->directory) + if (!m_returned_parent) + { + // return a fake entry representing the parent before anything else + m_returned_parent = true; + m_synthetic_entry.name = ".."; + m_synthetic_entry.type = osd::directory::entry::entry_type::DIR; + m_synthetic_entry.size = 0; // FIXME: what would stat say? + // FIXME: modified time? + return &m_synthetic_entry; + } + else { - err = osd_file::error::FAILURE; - goto done; + return read_excluding_parent(); } + } + + osd::directory::entry m_synthetic_entry; + +private: + virtual osd::directory::entry const *read_excluding_parent() = 0; + + bool m_returned_parent; +}; + - /* is this path the root? if so, pretend we've already returned the parent */ - if (is_root(path.c_str())) - result->returned_parent = true; +class archive_directory_impl : public zippath_directory_base +{ +public: + archive_directory_impl(archive_file::ptr &&zipfile, std::string &&zipprefix) : + zippath_directory_base(true), + m_zipfile(std::move(zipfile)), + m_zipprefix(std::move(zipprefix)) + { } -done: - if ((directory == nullptr || err != osd_file::error::NONE) && result != nullptr) + virtual bool is_archive() const override { return true; } + +private: + virtual osd::directory::entry const *read_excluding_parent() override + { + osd::directory::entry const *result = nullptr; + char const *relpath = nullptr; + do + { + // a zip file read + int header; + do + { + header = m_called_zip_first ? m_zipfile->next_file() : m_zipfile->first_file(); + m_called_zip_first = true; + relpath = nullptr; + } + while ((header >= 0) && ((relpath = get_relative_path()) == nullptr)); + + if (relpath) + { + // we've found a ZIP entry; but this may be an entry deep within the target directory + char const *separator = relpath; + while (*separator && !is_zip_file_separator(*separator)) separator++; + + if (*separator || m_zipfile->current_is_directory()) + { + // a nested entry; loop through returned_dirlist to see if we've returned the parent directory + auto const len(separator - relpath); + auto rdent = m_returned_dirlist.begin(); + while (m_returned_dirlist.end() != rdent) + { + if ((rdent->length() == len) && !core_strnicmp(rdent->c_str(), relpath, len)) + break; + else + ++rdent; + } + + if (m_returned_dirlist.end() == rdent) + { + // we've found a new directory; add this to returned_dirlist + m_returned_dirlist.emplace_front(relpath, separator - relpath); + + // ...and return it + m_synthetic_entry.name = m_returned_dirlist.front().c_str(); + m_synthetic_entry.type = osd::directory::entry::entry_type::DIR; + m_synthetic_entry.size = 0; // FIXME: what would stat say? + // FIXME: modified time? + result = &m_synthetic_entry; + } + } + else + { + // a real file + m_synthetic_entry.name = relpath; + m_synthetic_entry.type = osd::directory::entry::entry_type::FILE; + m_synthetic_entry.size = m_zipfile->current_uncompressed_length(); + m_synthetic_entry.last_modified = m_zipfile->current_last_modified(); + result = &m_synthetic_entry; + } + } + } + while (relpath && !result); + return result; + } + + + // ------------------------------------------------- + // get_relative_path - checks to see if a specified + // header is in the zippath_directory, and if so + // returns the relative path + // ------------------------------------------------- + + char const *get_relative_path() const + { + std::string::size_type len = m_zipprefix.length(); + char const *prefix = m_zipprefix.c_str(); + while (is_zip_file_separator(*prefix)) + { + len--; + prefix++; + } + + std::string const ¤t(m_zipfile->current_name()); + char const *result = current.c_str() + len; + if ((current.length() >= len) && + !strncmp(prefix, current.c_str(), len) && + (!*prefix || is_zip_file_separator(*result) || is_zip_file_separator(m_zipprefix.back()))) + { + while (is_zip_file_separator(*result)) + result++; + + return *result ? result : nullptr; + } + else + { + return nullptr; + } + } + + + bool m_called_zip_first = false; + archive_file::ptr const m_zipfile; + std::string const m_zipprefix; + std::forward_list<std::string> m_returned_dirlist; +}; + + +class filesystem_diectory_impl : public zippath_directory_base +{ +public: + filesystem_diectory_impl(std::string const &path) : zippath_directory_base(!is_root(path)), m_directory(osd::directory::open(path)) + { + } + + virtual bool is_archive() const override { return false; } + + explicit operator bool() const { return bool(m_directory); } + +private: + virtual osd::directory::entry const *read_excluding_parent() override + { + assert(m_directory); + + // a normal directory read + osd::directory::entry const *result = nullptr; + do + { + result = m_directory->read(); + } + while (result && (!strcmp(result->name, ".") || !strcmp(result->name, ".."))); + + // special case - is this entry a ZIP file? if so we need to return it as a "directory" + if (result && (is_zip_file(result->name) || is_7z_file(result->name))) + { + // copy; but change the entry type + m_synthetic_entry = *result; + m_synthetic_entry.type = osd::directory::entry::entry_type::DIR; + m_synthetic_entry.size = 0; // FIXME: what would stat say? + return &m_synthetic_entry; + } + else + { + return result; + } + } + + osd::directory::ptr const m_directory; +}; + +} // anonymous namespace + + +// ------------------------------------------------- +// zippath_directory::open - opens a directory +// ------------------------------------------------- + +osd_file::error zippath_directory::open(std::string const &path, ptr &directory) +{ + try + { + // resolve the path + osd::directory::entry::entry_type entry_type; + archive_file::ptr zipfile; + std::string zipprefix; + osd_file::error const err = zippath_resolve(path.c_str(), entry_type, zipfile, zipprefix); + if (osd_file::error::NONE != err) + return err; + + // we have to be a directory + if (osd::directory::entry::entry_type::DIR != entry_type) + return osd_file::error::NOT_FOUND; + + // was the result a ZIP? + if (zipfile) + { + directory = std::make_unique<archive_directory_impl>(std::move(zipfile), std::move(zipprefix)); + return osd_file::error::NONE; + } + else + { + // a conventional directory + std::unique_ptr<filesystem_diectory_impl> result(new filesystem_diectory_impl(path)); + if (!*result) + return osd_file::error::FAILURE; + + directory = std::move(result); + return osd_file::error::NONE; + } + } + catch (std::bad_alloc const &) { - zippath_closedir(result); - result = nullptr; + return osd_file::error::OUT_OF_MEMORY; } - if (directory != nullptr) - *directory = result; - return err; } // ------------------------------------------------- -// zippath_closedir - closes a directory +// zippath_directory::~zippath_directory - closes +// a directory +// ------------------------------------------------- + +zippath_directory::~zippath_directory() +{ +} + + +// ------------------------------------------------- +// zippath_parent - retrieves the parent directory +// ------------------------------------------------- + +std::string &zippath_parent(std::string &dst, const std::string &path) +{ + std::string::size_type pos; + parse_parent_path(path, &pos, nullptr); + + if (pos != std::string::npos) + dst = path.substr(0, pos + 1); + else + dst.clear(); + return dst; +} + + + +// ------------------------------------------------- +// zippath_parent - retrieves the parent directory +// ------------------------------------------------- + +std::string zippath_parent(const std::string &path) +{ + std::string result; + zippath_parent(result, path); + return result; +} + + + +// ------------------------------------------------- +// zippath_parent_basename - retrieves the parent +// directory basename // ------------------------------------------------- /** - * @fn void zippath_closedir(zippath_directory *directory) + * @fn std::string &zippath_parent_basename(std::string &dst, const std::string &path) * - * @brief Zippath closedir. + * @brief Zippath parent basename. * - * @param [in,out] directory If non-null, pathname of the directory. + * @param [in,out] dst Destination for the. + * @param path Full pathname of the file. + * + * @return A std::string& */ -void zippath_closedir(zippath_directory *directory) +std::string &zippath_parent_basename(std::string &dst, const std::string &path) { - if (directory->directory != nullptr) - directory->directory.reset(); + std::string::size_type beginpos, endpos; + parse_parent_path(path, &beginpos, &endpos); + dst.copy((char*)(path.c_str() + beginpos + 1), endpos - beginpos); + return dst; +} + - if (directory->zipfile != nullptr) - directory->zipfile.reset(); - directory->returned_dirlist.clear(); +// ------------------------------------------------- +// zippath_parent_basename - retrieves the parent +// directory basename +// ------------------------------------------------- - delete directory; +std::string zippath_parent_basename(const std::string &path) +{ + std::string result; + zippath_parent_basename(result, path); + return result; } + // ------------------------------------------------- -// get_relative_path - checks to see if a specified -// header is in the zippath_directory, and if so -// returns the relative path +// zippath_combine - combines two paths // ------------------------------------------------- /** - * @fn static const char *get_relative_path(zippath_directory *directory, const zip_file_header *header) + * @fn std::string &zippath_combine(std::string &dst, const char *path1, const char *path2) * - * @brief Gets relative path. + * @brief Zippath combine. * - * @param [in,out] directory If non-null, pathname of the directory. - * @param header The header. + * @param [in,out] dst Destination for the. + * @param path1 The first path. + * @param path2 The second path. * - * @return null if it fails, else the relative path. + * @return A std::string& */ -static const char *get_relative_path(zippath_directory const &directory) +std::string &zippath_combine(std::string &dst, const std::string &path1, const std::string &path2) { - auto len = directory.zipprefix.length(); - char const *prefix = directory.zipprefix.c_str(); - while (is_zip_file_separator(*prefix)) + if (path2 == ".") { - len--; - prefix++; + dst.assign(path1); } - - std::string const ¤t(directory.zipfile->current_name()); - char const *result = directory.zipfile->current_name().c_str() + len; - if ((current.length() >= len) && - !strncmp(prefix, current.c_str(), len) && - (!*prefix || is_zip_file_separator(*result) || is_zip_file_separator(directory.zipprefix.back()))) + else if (path2 == "..") { - while (is_zip_file_separator(*result)) - result++; - - return *result ? result : nullptr; + dst = zippath_parent(path1); + } + else if (osd_is_absolute_path(path2)) + { + dst.assign(path2); + } + else if (!path1.empty() && !is_path_separator(*path1.rbegin())) + { + dst.assign(path1).append(PATH_SEPARATOR).append(path2); } else { - return nullptr; + dst.assign(path1).append(path2); } + return dst; +} + + + +// ------------------------------------------------- +// zippath_combine - combines two paths +// ------------------------------------------------- + +std::string zippath_combine(const std::string &path1, const std::string &path2) +{ + std::string result; + zippath_combine(result, path1, path2); + return result; } + +/*************************************************************************** + FILE OPERATIONS +***************************************************************************/ + // ------------------------------------------------- -// zippath_readdir - reads a directory +// zippath_fopen - opens a zip path file // ------------------------------------------------- /** - * @fn const osd::directory::entry *zippath_readdir(zippath_directory *directory) + * @fn osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path) * - * @brief Zippath readdir. + * @brief Zippath fopen. * - * @param [in,out] directory If non-null, pathname of the directory. + * @param filename Filename of the file. + * @param openflags The openflags. + * @param [in,out] file [in,out] If non-null, the file. + * @param [in,out] revised_path Full pathname of the revised file. * - * @return null if it fails, else an osd::directory::entry*. + * @return A osd_file::error. */ -const osd::directory::entry *zippath_readdir(zippath_directory *directory) +osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path) { - const osd::directory::entry *result = nullptr; + osd_file::error filerr = osd_file::error::NOT_FOUND; + archive_file::error ziperr; + archive_file::ptr zip; + int header; + osd::directory::entry::entry_type entry_type; + int len; - if (!directory->returned_parent) - { - /* first thing's first - return parent directory */ - directory->returned_parent = true; - directory->returned_entry.name = ".."; - directory->returned_entry.type = osd::directory::entry::entry_type::DIR; - directory->returned_entry.size = 0; // FIXME: what would stat say? - // FIXME: modified time? - result = &directory->returned_entry; - } - else if (directory->directory) - { - /* a normal directory read */ - do - { - result = directory->directory->read(); - } - while (result && (!strcmp(result->name, ".") || !strcmp(result->name, ".."))); + /* first, set up the two types of paths */ + std::string mainpath(filename); + std::string subpath; + file = nullptr; - /* special case - is this entry a ZIP file? if so we need to return it as a "directory" */ - if (result && (is_zip_file(result->name) || is_7z_file(result->name))) - { - /* copy; but change the entry type */ - directory->returned_entry = *result; - directory->returned_entry.type = osd::directory::entry::entry_type::DIR; - directory->returned_entry.size = 0; // FIXME: what would stat say? - // FIXME: modified time? - result = &directory->returned_entry; - } - } - else if (directory->zipfile) + /* loop through */ + while((file == nullptr) && (mainpath.length() > 0) + && ((openflags == OPEN_FLAG_READ) || (subpath.length() == 0))) { - char const *relpath; - do + /* is the mainpath a ZIP path? */ + if (is_zip_file(mainpath) || is_7z_file(mainpath)) { - /* a zip file read */ - int header; - do - { - header = directory->called_zip_first ? directory->zipfile->next_file() : directory->zipfile->first_file(); - directory->called_zip_first = true; - relpath = nullptr; - } - while ((header >= 0) && ((relpath = get_relative_path(*directory)) == nullptr)); - - if (relpath) + /* this file might be a zip file - lets take a look */ + ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip); + if (ziperr == archive_file::error::NONE) { - /* we've found a ZIP entry; but this may be an entry deep within the target directory */ - char const *separator = relpath; - while (*separator && !is_zip_file_separator(*separator)) separator++; - - if (*separator || directory->zipfile->current_is_directory()) + /* it is a zip file - error if we're not opening for reading */ + if (openflags != OPEN_FLAG_READ) { - /* a nested entry; loop through returned_dirlist to see if we've returned the parent directory */ - auto const len(separator - relpath); - auto rdent = directory->returned_dirlist.begin(); - while (directory->returned_dirlist.end() != rdent) - { - if ((rdent->length() == len) && !core_strnicmp(rdent->c_str(), relpath, len)) - break; - else - ++rdent; - } - - if (directory->returned_dirlist.end() == rdent) - { - /* we've found a new directory; add this to returned_dirlist */ - directory->returned_dirlist.emplace_front(relpath, separator - relpath); - - /* ...and return it */ - directory->returned_entry.name = directory->returned_dirlist.front().c_str(); - directory->returned_entry.type = osd::directory::entry::entry_type::DIR; - directory->returned_entry.size = 0; // FIXME: what would stat say? - // FIXME: modified time? - result = &directory->returned_entry; - } + filerr = osd_file::error::ACCESS_DENIED; + goto done; } + + if (subpath.length() > 0) + header = zippath_find_sub_path(*zip, subpath, entry_type); else + header = zip->first_file(); + + if (header < 0) { - /* a real file */ - directory->returned_entry.name = relpath; - directory->returned_entry.type = osd::directory::entry::entry_type::FILE; - directory->returned_entry.size = directory->zipfile->current_uncompressed_length(); - directory->returned_entry.last_modified = directory->zipfile->current_last_modified(); - result = &directory->returned_entry; + filerr = osd_file::error::NOT_FOUND; + goto done; } + + /* attempt to read the file */ + filerr = create_core_file_from_zip(*zip, file); + if (filerr != osd_file::error::NONE) + goto done; + + /* update subpath, if appropriate */ + if (subpath.length() == 0) + subpath.assign(zip->current_name()); + + /* we're done */ + goto done; } } - while (relpath && !result); - } - return result; -} + if (subpath.length() == 0) + filerr = util::core_file::open(filename, openflags, file); + else + filerr = osd_file::error::NOT_FOUND; + /* if we errored, then go up a directory */ + if (filerr != osd_file::error::NONE) + { + /* go up a directory */ + auto temp = zippath_parent(mainpath); -// ------------------------------------------------- -// zippath_is_zip - returns true if this path is -// a ZIP path or false if not -// ------------------------------------------------- + /* append to the sub path */ + if (subpath.length() > 0) + { + std::string temp2; + mainpath = mainpath.substr(temp.length()); + temp2.assign(mainpath).append(PATH_SEPARATOR).append(subpath); + subpath.assign(temp2); + } + else + { + mainpath = mainpath.substr(temp.length()); + subpath.assign(mainpath); + } + /* get the new main path, truncating path separators */ + len = temp.length(); + while (len > 0 && is_zip_file_separator(temp[len - 1])) + len--; + temp = temp.substr(0, len); + mainpath.assign(temp); + } + } -/** - * @fn int zippath_is_zip(zippath_directory *directory) - * - * @brief Zippath is zip. - * - * @param [in,out] directory If non-null, pathname of the directory. - * - * @return An int. - */ +done: + /* store the revised path */ + revised_path.clear(); + if (filerr == osd_file::error::NONE) + { + /* cannonicalize mainpath */ + std::string alloc_fullpath; + filerr = osd_get_full_path(alloc_fullpath, mainpath); + if (filerr == osd_file::error::NONE) + { + revised_path = alloc_fullpath; + if (subpath.length() > 0) + revised_path.append(PATH_SEPARATOR).append(subpath); + } + } -bool zippath_is_zip(zippath_directory *directory) -{ - return directory->zipfile != nullptr; + return filerr; } } // namespace util diff --git a/src/lib/util/zippath.h b/src/lib/util/zippath.h index 3bef7fc9d39..cf6c2249f9c 100644 --- a/src/lib/util/zippath.h +++ b/src/lib/util/zippath.h @@ -8,22 +8,40 @@ ***************************************************************************/ -#pragma once - #ifndef MAME_LIB_UTIL_ZIPPATH_H #define MAME_LIB_UTIL_ZIPPATH_H +#pragma once + #include "corefile.h" -#include <string> #include "unzip.h" +#include <string> + namespace util { + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ -class zippath_directory; +class zippath_directory +{ +public: + typedef std::unique_ptr<zippath_directory> ptr; + + // opens a directory + static osd_file::error open(std::string const &path, ptr &directory); + + // closes a directory + virtual ~zippath_directory(); + + // reads a directory entry + virtual osd::directory::entry const *readdir() = 0; + + // returns true if this directory is an archive or false if it is a filesystem directory + virtual bool is_archive() const = 0; +}; @@ -51,22 +69,6 @@ std::string zippath_combine(const std::string &path1, const std::string &path2); // opens a zip path file osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path); - -// ----- directory operations ----- */ - -// opens a directory -osd_file::error zippath_opendir(const std::string &path, zippath_directory **directory); - -// closes a directory -void zippath_closedir(zippath_directory *directory); - -// reads a directory entry -const osd::directory::entry *zippath_readdir(zippath_directory *directory); - -// returns true if this path is a ZIP path or false if not -bool zippath_is_zip(zippath_directory *directory); - } // namespace util - #endif // MAME_LIB_UTIL_ZIPPATH_H diff --git a/src/osd/modules/debugger/debugimgui.cpp b/src/osd/modules/debugger/debugimgui.cpp index 11e19b261eb..7a5f098e5b4 100644 --- a/src/osd/modules/debugger/debugimgui.cpp +++ b/src/osd/modules/debugger/debugimgui.cpp @@ -959,7 +959,10 @@ void debug_imgui::mount_image() { case file_entry_type::DRIVE: case file_entry_type::DIRECTORY: - err = util::zippath_opendir(m_selected_file->fullpath.c_str(), nullptr); + { + util::zippath_directory::ptr dir; + err = util::zippath_directory::open(m_selected_file->fullpath.c_str(), dir); + } if(err == osd_file::error::NONE) { m_filelist_refresh = true; @@ -994,22 +997,19 @@ void debug_imgui::create_image() void debug_imgui::refresh_filelist() { - int x; - osd_file::error err; - util::zippath_directory* dir = nullptr; - const char *volume_name; - const osd::directory::entry *dirent; uint8_t first = 0; // todo m_filelist.clear(); m_filelist_refresh = false; - err = util::zippath_opendir(m_path,&dir); + util::zippath_directory::ptr dir; + osd_file::error const err = util::zippath_directory::open(m_path,dir); if(err == osd_file::error::NONE) { - x = 0; + int x = 0; // add drives + const char *volume_name; while((volume_name = osd_get_volume_name(x))!=nullptr) { file_entry temp; @@ -1020,7 +1020,8 @@ void debug_imgui::refresh_filelist() x++; } first = m_filelist.size(); - while((dirent = util::zippath_readdir(dir)) != nullptr) + const osd::directory::entry *dirent; + while((dirent = dir->readdir()) != nullptr) { file_entry temp; switch(dirent->type) @@ -1039,8 +1040,7 @@ void debug_imgui::refresh_filelist() m_filelist.emplace_back(std::move(temp)); } } - if (dir != nullptr) - util::zippath_closedir(dir); + dir.reset(); // sort file list, as it is not guaranteed to be in any particular order std::sort(m_filelist.begin()+first,m_filelist.end(),[](file_entry x, file_entry y) { return x.basename < y.basename; } ); |