ml; charset=UTF-8 Last-Modified: Mon, 05 May 2025 02:48:04 GMT Expires: Thu, 03 May 2035 02:48:04 GMT mame - MAME - Multiple Arcade Machine Emulator
summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emu/render.cpp8
-rw-r--r--src/mame/layout/sc2prem2.lay367
2 files changed, 347 insertions, 28 deletions
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index 42a837062a3..2790c213cf5 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -2554,8 +2554,8 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob
// get the scaled texture and append it
float const xsize(item.scroll_size_x());
float const ysize(item.scroll_size_y());
- s32 texwidth = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight) / xsize);
- s32 texheight = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth) / ysize);
+ s32 texwidth = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth) / xsize);
+ s32 texheight = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight) / ysize);
texwidth = (std::min)(texwidth, m_maxtexwidth);
texheight = (std::min)(texheight, m_maxtexheight);
texture->get_scaled(texwidth, texheight, prim->texture, list, prim->flags);
@@ -2564,8 +2564,8 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob
render_bounds cliprect = prim->bounds & m_bounds;
// determine UV coordinates and apply clipping
- float const xwindow((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight);
- float const ywindow((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth);
+ float const xwindow((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth);
+ float const ywindow((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight);
float const xrange(float(texwidth) - (item.scroll_wrap_x() ? 0.0f : xwindow));
float const yrange(float(texheight) - (item.scroll_wrap_y() ? 0.0f : ywindow));
float const xoffset(render_round_nearest(item.scroll_pos_x() * xrange) / float(texwidth));
diff --git a/src/mame/layout/sc2prem2.lay b/src/mame/layout/sc2prem2.lay
index 403b9f01279..3904d08dad9 100644
--- a/src/mame/layout/sc2prem2.lay
+++ b/src/mame/layout/sc2prem2.lay
@@ -1772,30 +1772,339 @@
<color red="1.0" green="1.0" blue="1.0"/>
</rect>
</element>
- <element name="reel0" defstate="0">
- <reel reelreversed="1" stateoffset="2730" numsymbolsvisible="3" symbollist="Bell,Cherry(*),Lemon,Orange,Bar,Cherry,Grape,Orange,Melon,Orange,Cherry(*),Orange,Cashpot,Cherry,Lemon,Cherry">
+ <element name="reel0">
+ <rect>
+ <bounds x="0" y="0" width="80" height="1024"/>
+ <color alpha="0.0"/>
+ </rect>
+ <text string="Bell">
+ <bounds x="4" y="8" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry(*)">
+ <bounds x="4" y="72" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon">
+ <bounds x="4" y="136" width="72" height="48"/>
<color red="0.0" green="0.0" blue="0.0"/>
- </reel>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="200" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Bar">
+ <bounds x="4" y="264" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="328" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Grape">
+ <bounds x="4" y="392" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="456" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Melon">
+ <bounds x="4" y="520" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="584" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry(*)">
+ <bounds x="4" y="648" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="712" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cashpot">
+ <bounds x="4" y="776" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="840" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon(*)">
+ <bounds x="4" y="904" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="968" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
</element>
- <element name="reel1" defstate="0">
- <reel reelreversed="1" stateoffset="2730" numsymbolsvisible="3" symbollist="Bell,Cherry,Orange,Melon,Bar,Grape,Cherry(*),Orange,Cherry,Melon,Grape,Cashpot,Lemon,Cherry,Lemon(*),Orange">
+ <element name="reel1">
+ <rect>
+ <bounds x="0" y="0" width="80" height="1024"/>
+ <color alpha="0.0"/>
+ </rect>
+ <text string="Bell">
+ <bounds x="4" y="8" width="72" height="48"/>
<color red="0.0" green="0.0" blue="0.0"/>
- </reel>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="72" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="136" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Melon">
+ <bounds x="4" y="200" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Bar">
+ <bounds x="4" y="264" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Grape">
+ <bounds x="4" y="328" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry(*)">
+ <bounds x="4" y="392" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="456" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="520" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Melon">
+ <bounds x="4" y="584" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Grape">
+ <bounds x="4" y="648" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cashpot">
+ <bounds x="4" y="712" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon">
+ <bounds x="4" y="776" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="840" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon(*)">
+ <bounds x="4" y="904" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="968" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
</element>
- <element name="reel2" defstate="0">
- <reel reelreversed="1" stateoffset="2730" numsymbolsvisible="3" symbollist="Bell,Cherry,Lemon,Orange(*),Bar,Grape,Orange,Cherry,Melon,Cherry,Grape,Orange(*),Lemon,Cashpot,Lemon,Cherry">
+ <element name="reel2">
+ <rect>
+ <bounds x="0" y="0" width="80" height="1024"/>
+ <color alpha="0.0"/>
+ </rect>
+ <text string="Bell">
+ <bounds x="4" y="8" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="72" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon">
+ <bounds x="4" y="136" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange(*)">
+ <bounds x="4" y="200" width="72" height="48"/>
<color red="0.0" green="0.0" blue="0.0"/>
- </reel>
+ </text>
+ <text string="Bar">
+ <bounds x="4" y="264" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Grape">
+ <bounds x="4" y="328" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="392" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="456" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Melon">
+ <bounds x="4" y="520" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="584" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Grape">
+ <bounds x="4" y="648" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange(*)">
+ <bounds x="4" y="712" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon">
+ <bounds x="4" y="776" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cashpot">
+ <bounds x="4" y="840" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon">
+ <bounds x="4" y="904" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="968" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
</element>
- <element name="reel3" defstate="0">
- <reel reelreversed="1" stateoffset="2730" numsymbolsvisible="3" symbollist="Bell,Cherry,Lemon,Grape,Bar,Lemon(*),Grape,Orange,Melon,Cherry,Cashpot,Orange,Lemon,Cherry(*),Lemon,Cherry">
+ <element name="reel3">
+ <rect>
+ <bounds x="0" y="0" width="80" height="1024"/>
+ <color alpha="0.0"/>
+ </rect>
+ <text string="Bell">
+ <bounds x="4" y="8" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="72" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon">
+ <bounds x="4" y="136" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Grape">
+ <bounds x="4" y="200" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Bar">
+ <bounds x="4" y="264" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon(*)">
+ <bounds x="4" y="328" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Grape">
+ <bounds x="4" y="392" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="456" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Melon">
+ <bounds x="4" y="520" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="584" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cashpot">
+ <bounds x="4" y="648" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Orange">
+ <bounds x="4" y="712" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Lemon">
+ <bounds x="4" y="776" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry(*)">
+ <bounds x="4" y="840" width="72" height="48"/>
<color red="0.0" green="0.0" blue="0.0"/>
- </reel>
+ </text>
+ <text string="Lemon">
+ <bounds x="4" y="904" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="Cherry">
+ <bounds x="4" y="968" width="72" height="48"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
</element>
<element name="reel4" defstate="0">
- <reel reelreversed="0" stateoffset="-1365" numsymbolsvisible="1" symbollist="6,1,2,3,4,5,6,1,2,3,4,5">
+ <rect>
+ <bounds x="0" y="0" width="70" height="840"/>
+ <color alpha="0.0"/>
+ </rect>
+ <text string="6">
+ <bounds x="5" y="5" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="1">
+ <bounds x="5" y="75" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="2">
+ <bounds x="5" y="145" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="3">
+ <bounds x="5" y="215" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="4">
+ <bounds x="5" y="285" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="5">
+ <bounds x="5" y="355" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="6">
+ <bounds x="5" y="425" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="1">
+ <bounds x="5" y="495" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="2">
+ <bounds x="5" y="565" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text string="3">
+ <bounds x="5" y="635" width="60" height="60"/>
<color red="0.0" green="0.0" blue="0.0"/>
- </reel>
+ </text>
+ <text string="4">
+ <bounds x="5" y="705" width="60" height="60"/>
+ <color red="0.0" green="0.0" blue="0.0"/>
+ </text>
+ <text s
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************

    zippath.c

    File/directory/path operations that work with ZIP files

***************************************************************************/

#include "zippath.h"

#include "corestr.h"
#include "unzip.h"

#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstdlib>
#include <forward_list>
#include <new>


namespace util {

namespace {

/**
 * @fn  int is_path_separator(char c)
 *
 * @brief   ============================================================
 *            is_path_separator
 *          ============================================================.
 *
 * @param   c   The character.
 *
 * @return  An int.
 */

int is_path_separator(char c)
{
	// FIXME: don't assume DOS-like behaviour - backslashes are valid filename characters on *NIX
	return (c == '/') || (c == '\\');
}

// -------------------------------------------------
//  is_root - tests to see if this path is the root
// -------------------------------------------------

bool is_root(std::string_view path)
{
#if defined(_WIN32)
	// FIXME: don't assume paths are DOS-like - UNC paths, \\?\ long path prefix, etc. complicate this

	// skip drive letter
	if (path.length() >= 2 && isalpha(path[0]) && (path[1] == ':'))
		path.remove_prefix(2);

	// skip path separators
	return path.find_first_not_of(PATH_SEPARATOR) == std::string_view::npos;
#else
	return path.find_first_not_of(PATH_SEPARATOR) == std::string_view::npos;
#endif
}



// -------------------------------------------------
//  is_7z_file - tests to see if this file is a
//  7-zip file
// -------------------------------------------------

bool is_7z_file(std::string_view path)
{
	return core_filename_ends_with(path, ".7z");
}


// -------------------------------------------------
//  is_zip_file - tests to see if this file is a
//  ZIP file
// -------------------------------------------------

bool is_zip_file(std::string_view path)
{
	return core_filename_ends_with(path, ".zip") || core_filename_ends_with(path, ".imz");
}



// -------------------------------------------------
//  is_zip_file_separator - returns whether this
//  character is a path separator within a ZIP file
// -------------------------------------------------

bool is_zip_file_separator(char c)
{
	return (c == '/') || (c == '\\');
}



// -------------------------------------------------
//  is_zip_path_separator - returns whether this
//  character is a path separator within a ZIP path
// -------------------------------------------------

bool is_zip_path_separator(char c)
{
	return is_zip_file_separator(c) || is_path_separator(c);
}



// -------------------------------------------------
//  next_path_char - lexes out the next path
//  character, normalizing separators as '/'
// -------------------------------------------------

char next_path_char(std::string_view s, std::string_view::size_type &pos)
{
	// skip over any initial separators
	if (pos == 0)
	{
		while ((pos < s.length()) && is_zip_file_separator(s[pos]))
			pos++;
	}

	// are we at a path separator?
	if (pos == s.length())
	{
		// return NUL
		return '\0';
	}
	else if (is_zip_file_separator(s[pos]))
	{
		// skip over path separators
		while((pos < s.length()) && is_zip_file_separator(s[pos]))
			pos++;

		// normalize as '/'
		return '/';
	}
	else
	{
		// return character
		return std::tolower(s[pos++]);
	}
}




// -------------------------------------------------
//  zippath_find_sub_path - attempts to identify the
//  type of a sub path in a zip file
// -------------------------------------------------

int zippath_find_sub_path(archive_file &zipfile, std::string_view subpath, osd::directory::entry::entry_type &type)
{
	for (int header = zipfile.first_file(); header >= 0; header = zipfile.next_file())
	{
		std::string_view::size_type i = 0, j = 0;
		char c1, c2;
		do
		{
			c1 = next_path_char(zipfile.current_name(), i);
			c2 = next_path_char(subpath, j);
		}
		while ((c1 == c2) && c1 && c2);

		if (!c2 || ((c2 == '/') && !(c2 = next_path_char(subpath, j))))
		{
			if (!c1)
			{
				type = zipfile.current_is_directory() ? osd::directory::entry::entry_type::DIR : osd::directory::entry::entry_type::FILE;
				return header;
			}
			else if ((c1 == '/') || (i <= 1U))
			{
				type = osd::directory::entry::entry_type::DIR;
				return header;
			}
		}
	}

	type = osd::directory::entry::entry_type::NONE;
	return -1;
}



// -------------------------------------------------
//  zippath_resolve - separates a ZIP path out into
//  true path and ZIP entry components
// -------------------------------------------------

std::error_condition zippath_resolve(std::string_view path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath)
{
	newpath.clear();

	// be conservative
	zipfile.reset();

	std::string apath(path);
	std::string apath_trimmed;
	osd::directory::entry::entry_type current_entry_type = osd::directory::entry::entry_type::NONE;
	bool went_up = false;
	do
	{
		// trim the path of trailing path separators
		auto const i = apath.find_last_not_of(PATH_SEPARATOR);
		if (i != std::string::npos)
			apath.erase(std::max<decltype(i)>(i + 1, 2)); // don't erase drive letter
		else if (!is_root(apath))
			break;

		apath_trimmed = apath;

		// stat the path
		auto current_entry = osd_stat(apath_trimmed);

		// did we find anything?
		if (current_entry)
		{
			// get the entry type and free the stat entry
			current_entry_type = current_entry->type;
		}
		else
		{
			// if we have not found the file or directory, go up
			went_up = true;
			apath = zippath_parent(apath);
		}
	}
	while ((current_entry_type == osd::directory::entry::entry_type::NONE) && !is_root(apath));

	// if we did not find anything, then error out
	if (current_entry_type == osd::directory::entry::entry_type::NONE)
	{
		entry_type = osd::directory::entry::entry_type::NONE;
		return std::errc::no_such_file_or_directory;
	}

	// is this file a ZIP file?
	if ((current_entry_type == osd::directory::entry::entry_type::FILE) &&
		((is_zip_file(apath_trimmed) && !archive_file::open_zip(apath_trimmed, zipfile)) ||
			(is_7z_file(apath_trimmed) && !archive_file::open_7z(apath_trimmed, zipfile))))
	{
		auto i = path.length() - apath.length();
		while ((i > 0) && is_zip_path_separator(path[apath.length() + i - 1]))
			i--;
		newpath.assign(path, apath.length(), i);

		// this was a true ZIP path - attempt to identify the type of path
		zippath_find_sub_path(*zipfile, newpath, current_entry_type);
		if (current_entry_type == osd::directory::entry::entry_type::NONE)
			return std::errc::no_such_file_or_directory;
	}
	else
	{
		// this was a normal path
		if (went_up)
			return std::errc::no_such_file_or_directory;

		newpath = path;
	}

	// success!
	entry_type = current_entry_type;
	return std::error_condition();
}


// -------------------------------------------------
//  create_core_file_from_zip - creates a core_file
//  from a zip file entry
// -------------------------------------------------

std::error_condition create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file)
{
	// TODO: would be more efficient if we could open a memory-based core_file with uninitialised contents and decompress into it
	std::error_condition filerr;

	void *ptr = malloc(zip.current_uncompressed_length());
	if (!ptr)
	{
		filerr = std::errc::not_enough_memory;
		goto done;
	}

	filerr = zip.decompress(ptr, zip.current_uncompressed_length());
	if (filerr)
		goto done;

	filerr = util::core_file::open_ram_copy(ptr, zip.current_uncompressed_length(), OPEN_FLAG_READ, file);
	if (filerr)
		goto done;

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

	virtual osd::directory::entry const *readdir() override
	{
		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
		{
			return read_excluding_parent();
		}
	}

	osd::directory::entry m_synthetic_entry;

private:
	virtual osd::directory::entry const *read_excluding_parent() = 0;

	bool m_returned_parent;
};


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))
	{
		for (char &ch : m_zipprefix)
			if (is_path_separator(ch))
				ch = '/';
	}

	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 &current(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 m_zipprefix;
	std::forward_list<std::string> m_returned_dirlist;
};


class filesystem_directory_impl : public zippath_directory_base
{
public:
	filesystem_directory_impl(std::string_view path) : zippath_directory_base(!is_root(path)), m_directory(osd::directory::open(std::string(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
// -------------------------------------------------

std::error_condition zippath_directory::open(std::string_view path, ptr &directory)
{
	try
	{
		// resolve the path
		osd::directory::entry::entry_type entry_type;
		archive_file::ptr zipfile;
		std::string zipprefix;
		std::error_condition const err = zippath_resolve(path, entry_type, zipfile, zipprefix);
		if (err)
			return err;

		// we have to be a directory
		if (osd::directory::entry::entry_type::DIR != entry_type)
			return std::errc::not_a_directory;

		// was the result a ZIP?
		if (zipfile)
		{
			directory = std::make_unique<archive_directory_impl>(std::move(zipfile), std::move(zipprefix));
			return std::error_condition();
		}
		else
		{
			// a conventional directory
			std::unique_ptr<filesystem_directory_impl> result(new filesystem_directory_impl(path));
			if (!*result)
				return std::errc::io_error; // TODO: any way to give a better error here?

			directory = std::move(result);
			return std::error_condition();
		}
	}
	catch (std::bad_alloc const &)
	{
		return std::errc::not_enough_memory;
	}
}


// -------------------------------------------------
//  zippath_directory::~zippath_directory - closes
//  a directory
// -------------------------------------------------

zippath_directory::~zippath_directory()
{
}


// -------------------------------------------------
//  zippath_parent - retrieves the parent directory
// -------------------------------------------------

std::string zippath_parent(std::string_view path)
{
	// skip over trailing path separators
	auto pos = std::find_if_not(path.rbegin(), path.rend(), &is_path_separator);

	// now skip until we find a path separator
	pos = std::find_if(pos, path.rend(), &is_path_separator);

	if (path.rend() != pos)
		return std::string(path.begin(), pos.base());
	else
		return std::string();
}



// -------------------------------------------------
//  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&amp;
 */

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.back()))
	{
		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
***************************************************************************/

// -------------------------------------------------
//  zippath_fopen - opens a zip path file
// -------------------------------------------------

/**
 * @fn  std::error_condition zippath_fopen(std::string_view 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.
 */

std::error_condition zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
{
	std::error_condition filerr = std::errc::no_such_file_or_directory;
	archive_file::ptr zip;

	// first, set up the two types of paths
	std::string mainpath(filename);
	std::string subpath;
	file = nullptr;

	// loop through
	while (!file && !mainpath.empty())
	{
		// 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
			std::error_condition const ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip);
			if (!ziperr)
			{
				osd::directory::entry::entry_type entry_type;
				int header;
				if (!subpath.empty())
				{
					header = zippath_find_sub_path(*zip, subpath, entry_type);
				}
				else
				{
					header = zip->first_file();
					entry_type = osd::directory::entry::entry_type::FILE;
				}

				if (header < 0)
				{
					if (openflags & OPEN_FLAG_CREATE)
						filerr = std::errc::permission_denied;
					else
						filerr = std::errc::no_such_file_or_directory;
					goto done;
				}
				else if (osd::directory::entry::entry_type::DIR == entry_type)
				{
					filerr = std::errc::is_a_directory;
					goto done;
				}
				else if (openflags & OPEN_FLAG_WRITE)
				{
					filerr = std::errc::permission_denied;
					goto done;
				}

				// attempt to read the file
				filerr = create_core_file_from_zip(*zip, file);
				if (filerr)
					goto done;

				// update subpath, if appropriate
				if (subpath.empty())
					subpath.assign(zip->current_name());

				// we're done
				goto done;
			}
		}

		if (subpath.empty())
			filerr = util::core_file::open(filename, openflags, file);
		else
			filerr = std::errc::no_such_file_or_directory;

		// if we errored, then go up a directory
		if (filerr)
		{
			// go up a directory
			auto temp = zippath_parent(mainpath);

			// append to the sub path
			if (!subpath.empty())
			{
				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
			auto 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)
	{
		// canonicalize mainpath
		std::string alloc_fullpath;
		filerr = osd_get_full_path(alloc_fullpath, mainpath);
		if (!filerr)
		{
			revised_path = alloc_fullpath;
			if (!subpath.empty())
				revised_path.append(PATH_SEPARATOR).append(subpath);
		}
	}

	return filerr;
}

} // namespace util