diff --git a/loader/include/Geode/cocos/cocoa/CCObject.h b/loader/include/Geode/cocos/cocoa/CCObject.h index 4037ba99..cf0958ba 100644 --- a/loader/include/Geode/cocos/cocoa/CCObject.h +++ b/loader/include/Geode/cocos/cocoa/CCObject.h @@ -173,14 +173,14 @@ typedef void (CCObject::*SEL_MenuHandler)(CCObject*); typedef void (CCObject::*SEL_EventHandler)(CCEvent*); typedef int (CCObject::*SEL_Compare)(CCObject*); -#define schedule_selector(_SELECTOR) (cocos2d::SEL_SCHEDULE)(&_SELECTOR) -#define callfunc_selector(_SELECTOR) (cocos2d::SEL_CallFunc)(&_SELECTOR) -#define callfuncN_selector(_SELECTOR) (cocos2d::SEL_CallFuncN)(&_SELECTOR) -#define callfuncND_selector(_SELECTOR) (cocos2d::SEL_CallFuncND)(&_SELECTOR) -#define callfuncO_selector(_SELECTOR) (cocos2d::SEL_CallFuncO)(&_SELECTOR) -#define menu_selector(_SELECTOR) (cocos2d::SEL_MenuHandler)(&_SELECTOR) -#define event_selector(_SELECTOR) (cocos2d::SEL_EventHandler)(&_SELECTOR) -#define compare_selector(_SELECTOR) (cocos2d::SEL_Compare)(&_SELECTOR) +#define schedule_selector(...) (cocos2d::SEL_SCHEDULE)(&__VA_ARGS__) +#define callfunc_selector(...) (cocos2d::SEL_CallFunc)(&__VA_ARGS__) +#define callfuncN_selector(...) (cocos2d::SEL_CallFuncN)(&__VA_ARGS__) +#define callfuncND_selector(...) (cocos2d::SEL_CallFuncND)(&__VA_ARGS__) +#define callfuncO_selector(...) (cocos2d::SEL_CallFuncO)(&__VA_ARGS__) +#define menu_selector(...) (cocos2d::SEL_MenuHandler)(&__VA_ARGS__) +#define event_selector(...) (cocos2d::SEL_EventHandler)(&__VA_ARGS__) +#define compare_selector(...) (cocos2d::SEL_Compare)(&__VA_ARGS__) // end of base_nodes group /// @} diff --git a/loader/include/Geode/utils/file.hpp b/loader/include/Geode/utils/file.hpp index 8d8f207b..749ba06b 100644 --- a/loader/include/Geode/utils/file.hpp +++ b/loader/include/Geode/utils/file.hpp @@ -23,17 +23,77 @@ namespace geode::utils::file { ghc::filesystem::path const& path, bool recursive = false ); - class UnzipImpl; + class GEODE_DLL Zip final { + public: + using Path = ghc::filesystem::path; + + private: + class Impl; + std::unique_ptr m_impl; + + Zip(); + Zip(std::unique_ptr&& impl); + + Result<> addAllFromRecurse( + Path const& dir, Path const& entry + ); + + public: + Zip(Zip const&) = delete; + Zip(Zip&& other); + ~Zip(); + + /** + * Create zipper for file + */ + static Result create(Path const& file); + + /** + * Path to the opened zip + */ + Path getPath() const; + + /** + * Add an entry to the zip with data + */ + Result<> add(Path const& entry, byte_array const& data); + /** + * Add an entry to the zip with string data + */ + Result<> add(Path const& entry, std::string const& data); + /** + * Add an entry to the zip from a file on disk. If you want to add the + * file with a different name, read it into memory first and add it + * with Zip::add + * @param file File on disk + * @param entryDir Folder to place the file in in the zip + */ + Result<> addFrom(Path const& file, Path const& entryDir = Path()); + /** + * Add an entry to the zip from a directory on disk + * @param entry Path in the zip + * @param dir Directory on disk + */ + Result<> addAllFrom(Path const& dir); + /** + * Add a folder entry to the zip. If you want to add a folder from disk, + * use Zip::addAllFrom + * @param entry Folder path in zip + */ + Result<> addFolder(Path const& entry); + }; class GEODE_DLL Unzip final { private: - UnzipImpl* m_impl; + class Impl; + std::unique_ptr m_impl; + + Unzip(); + Unzip(std::unique_ptr&& impl); public: - Unzip() = delete; Unzip(Unzip const&) = delete; Unzip(Unzip&& other); - Unzip(UnzipImpl* impl); ~Unzip(); using Path = ghc::filesystem::path; diff --git a/loader/src/utils/file.cpp b/loader/src/utils/file.cpp index 47cfe292..d73d6537 100644 --- a/loader/src/utils/file.cpp +++ b/loader/src/utils/file.cpp @@ -1,6 +1,7 @@ #include <../support/zip_support/ZipUtils.h> #include <../support/zip_support/ioapi.h> #include <../support/zip_support/unzip.h> +#include <../support/zip_support/zip.h> #include #include #include @@ -132,6 +133,8 @@ Result> utils::file::listFiles( return Ok(res); } +// Unzip + static constexpr auto MAX_ENTRY_PATH_LEN = 256; struct ZipEntry { @@ -141,7 +144,7 @@ struct ZipEntry { ZPOS64_T uncompressedSize; }; -class file::UnzipImpl final { +class file::Unzip::Impl final { public: using Path = Unzip::Path; @@ -210,6 +213,7 @@ public: res.resize(entry.uncompressedSize); auto size = unzReadCurrentFile(m_zip, res.data(), entry.uncompressedSize); if (size < 0 || size != entry.uncompressedSize) { + unzCloseCurrentFile(m_zip); return Err("Unable to extract entry"); } unzCloseCurrentFile(m_zip); @@ -225,22 +229,20 @@ public: return m_zipPath; } - UnzipImpl(unzFile zip, Path const& path) : m_zip(zip), m_zipPath(path) {} + Impl(unzFile zip, Path const& path) : m_zip(zip), m_zipPath(path) {} - ~UnzipImpl() { + ~Impl() { unzClose(m_zip); } }; -Unzip::Unzip(UnzipImpl* impl) : m_impl(impl) {} +Unzip::Unzip() : m_impl(nullptr) {} -Unzip::~Unzip() { - if (m_impl) { - delete m_impl; - } -} +Unzip::~Unzip() {} -Unzip::Unzip(Unzip&& other) : m_impl(other.m_impl) { +Unzip::Unzip(std::unique_ptr&& impl) : m_impl(std::move(impl)) {} + +Unzip::Unzip(Unzip&& other) : m_impl(std::move(other.m_impl)) { other.m_impl = nullptr; } @@ -250,19 +252,18 @@ Result Unzip::create(Path const& file) { if (!zip) { return Err("Unable to open zip file"); } - auto impl = new UnzipImpl(zip, file); + auto impl = std::make_unique(zip, file); if (!impl->loadEntries()) { - delete impl; return Err("Unable to read zip file"); } - return Ok(Unzip(impl)); + return Ok(Unzip(std::move(impl))); } -ghc::filesystem::path Unzip::getPath() const { +Unzip::Path Unzip::getPath() const { return m_impl->path(); } -std::vector Unzip::getEntries() const { +std::vector Unzip::getEntries() const { return map::keys(m_impl->entries()); } @@ -321,3 +322,127 @@ Result<> Unzip::intoDir( } return Ok(); } + +// Zip + +class file::Zip::Impl final { +public: + using Path = Unzip::Path; + +private: + zipFile m_zip; + Path m_zipPath; + +public: + Path& path() { + return m_zipPath; + } + + Result<> addFolder(Path const& path) { + // a directory in a zip is just a file with no data and which ends in + // a slash + auto strPath = path.generic_string(); + if (!strPath.ends_with("/") && !strPath.ends_with("\\")) { + strPath += "/"; + } + if (zipOpenNewFileInZip( + m_zip, strPath.c_str(), + nullptr, nullptr, 0, nullptr, 0, nullptr, + Z_DEFLATED, Z_DEFAULT_COMPRESSION + ) != ZIP_OK) { + return Err("Unable to create directory " + path.string()); + } + zipCloseFileInZip(m_zip); + return Ok(); + } + + Result<> add(Path const& path, byte_array const& data) { + // open entry + zip_fileinfo info = { 0 }; + if (zipOpenNewFileInZip( + m_zip, path.generic_string().c_str(), + &info, nullptr, 0, nullptr, 0, nullptr, + Z_DEFLATED, Z_DEFAULT_COMPRESSION + ) != ZIP_OK) { + return Err("Unable to create entry " + path.string()); + } + + // write data + if (zipWriteInFileInZip(m_zip, data.data(), data.size()) != ZIP_OK) { + zipCloseFileInZip(m_zip); + return Err("Unable to write entry " + path.string()); + } + + // make sure to close! + zipCloseFileInZip(m_zip); + + return Ok(); + } + + Impl(zipFile zip, Path const& path) : m_zip(zip), m_zipPath(path) {} + + ~Impl() { + zipClose(m_zip, nullptr); + } +}; + +Zip::Zip() : m_impl(nullptr) {} + +Zip::~Zip() {} + +Zip::Zip(std::unique_ptr&& impl) : m_impl(std::move(impl)) {} + +Zip::Zip(Zip&& other) : m_impl(std::move(other.m_impl)) { + other.m_impl = nullptr; +} + +Result Zip::create(Path const& file) { + // todo: make sure unicode paths work + auto zip = zipOpen(file.generic_string().c_str(), APPEND_STATUS_CREATE); + if (!zip) { + return Err("Unable to open zip file"); + } + auto impl = std::make_unique(zip, file); + return Ok(Zip(std::move(impl))); +} + +Zip::Path Zip::getPath() const { + return m_impl->path(); +} + +Result<> Zip::add(Path const& path, byte_array const& data) { + return m_impl->add(path, data); +} + +Result<> Zip::add(Path const& path, std::string const& data) { + return this->add(path, byte_array(data.begin(), data.end())); +} + +Result<> Zip::addFrom(Path const& file, Path const& entryDir) { + GEODE_UNWRAP_INTO(auto data, file::readBinary(file)); + return this->add(entryDir / file.filename(), data); +} + +Result<> Zip::addAllFromRecurse(Path const& dir, Path const& entry) { + GEODE_UNWRAP(this->addFolder(entry / dir.filename())); + for (auto& file : ghc::filesystem::directory_iterator(dir)) { + if (ghc::filesystem::is_directory(file)) { + GEODE_UNWRAP(this->addAllFromRecurse(file, entry / dir.filename())); + } else { + GEODE_UNWRAP_INTO(auto data, file::readBinary(file)); + GEODE_UNWRAP(this->addFrom(file, entry / dir.filename())); + } + } + return Ok(); +} + +Result<> Zip::addAllFrom(Path const& dir) { + if (!ghc::filesystem::is_directory(dir)) { + return Err("Path is not a directory"); + } + return this->addAllFromRecurse(dir, Path()); +} + +Result<> Zip::addFolder(Path const& entry) { + return m_impl->addFolder(entry); +}