From 1c26a14a90cf0a9346d286e5fce243953c024cc9 Mon Sep 17 00:00:00 2001 From: illlustr Date: Wed, 5 Feb 2025 14:40:45 +0700 Subject: [PATCH] add MiniZip Module --- modules/zip/SCsub | 9 ++ modules/zip/config.py | 14 ++++ modules/zip/doc_classes/ZIPPacker.xml | 74 +++++++++++++++++ modules/zip/doc_classes/ZIPReader.xml | 55 ++++++++++++ modules/zip/register_types.cpp | 42 ++++++++++ modules/zip/register_types.h | 32 +++++++ modules/zip/zip_packer.cpp | 105 +++++++++++++++++++++++ modules/zip/zip_packer.h | 69 ++++++++++++++++ modules/zip/zip_reader.cpp | 115 ++++++++++++++++++++++++++ modules/zip/zip_reader.h | 60 ++++++++++++++ 10 files changed, 575 insertions(+) create mode 100644 modules/zip/SCsub create mode 100644 modules/zip/config.py create mode 100644 modules/zip/doc_classes/ZIPPacker.xml create mode 100644 modules/zip/doc_classes/ZIPReader.xml create mode 100644 modules/zip/register_types.cpp create mode 100644 modules/zip/register_types.h create mode 100644 modules/zip/zip_packer.cpp create mode 100644 modules/zip/zip_packer.h create mode 100644 modules/zip/zip_reader.cpp create mode 100644 modules/zip/zip_reader.h diff --git a/modules/zip/SCsub b/modules/zip/SCsub new file mode 100644 index 000000000000..32e836adb2cb --- /dev/null +++ b/modules/zip/SCsub @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_zip = env_modules.Clone() + +# Module files +env_zip.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/zip/config.py b/modules/zip/config.py new file mode 100644 index 000000000000..997daa3c0fcf --- /dev/null +++ b/modules/zip/config.py @@ -0,0 +1,14 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + +def get_doc_classes(): + return [ + "ZipReader", + "ZipPacker", + ] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/zip/doc_classes/ZIPPacker.xml b/modules/zip/doc_classes/ZIPPacker.xml new file mode 100644 index 000000000000..e808287e345b --- /dev/null +++ b/modules/zip/doc_classes/ZIPPacker.xml @@ -0,0 +1,74 @@ + + + + Class for creating zip archives + + + ZIPPacker provides a way to create zip archives. + + + + + + + + + + + + + Opens a zip file to start writing. What happens if the file already exists is determined by [code]append[/code]. + + + + + + + + + Starts a new file in the zip archive. Subsequent calls to [method write_file] will write to this file. + If the file at the given path already exists, a duplicate entry will be created. + + + + + + + + + Writes data to a file in the archive. Which file is written to is determined by the latest call to [method start_file]. + If the file has already been written to, the data will be appended. + + + + + + + Closes the file that was opened with [method start_file]. + + + + + + + Closes the zip file and flushes the data to the disk. + + + + + + + + + + The file will be created if it does not exist, or overwritten if it does exist. + + + The zip file will be appended to the data in the file, if the file already exists. + + + Files will be added to the existing zip file. + Note: Files in the archive cannot be overwritten in this mode. If you write to a file that already exists in the archive, a duplicate entry will be created instead. + + + diff --git a/modules/zip/doc_classes/ZIPReader.xml b/modules/zip/doc_classes/ZIPReader.xml new file mode 100644 index 000000000000..bf3f7df71734 --- /dev/null +++ b/modules/zip/doc_classes/ZIPReader.xml @@ -0,0 +1,55 @@ + + + + Class for reading zip archives + + + ZIPReader provides a way to open zip archives and read the files inside. + + + + + + + + + + + Opens a zip file for reading. If a file was already open, it will be closed. + + + + + + + Returns the list of file paths in the archive. All paths are relative to the root of the archive. + For example, if you have an archive containg a folder, "foo", which contains a file, "bar.txt", the list will contain one item: "foo/bar.txt". + + + + + + + + + + + Returns the contents of a file in the archive as a [PoolByteArray]. If you need the contents as a string, use [method PoolByteArray.get_string_from_utf8]. + If there is a problem reading the file (such as the file doesn't exist), an empty PoolByteArray will be returned and an error will be logged to the console. + + + + + + + Closes the file. Subsequent operations other than [method open] will fail after this is called. + + + + + + + + + + diff --git a/modules/zip/register_types.cpp b/modules/zip/register_types.cpp new file mode 100644 index 000000000000..95f44fe476ca --- /dev/null +++ b/modules/zip/register_types.cpp @@ -0,0 +1,42 @@ +/**************************************************************************/ +/* register_types.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "register_types.h" + +#include "zip_packer.h" +#include "zip_reader.h" + +void register_zip_types() { + ClassDB::register_class(); + ClassDB::register_class(); +} + +void unregister_zip_types() { +} diff --git a/modules/zip/register_types.h b/modules/zip/register_types.h new file mode 100644 index 000000000000..00e8b69c1c64 --- /dev/null +++ b/modules/zip/register_types.h @@ -0,0 +1,32 @@ +/**************************************************************************/ +/* register_types.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +void register_zip_types(); +void unregister_zip_types(); diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp new file mode 100644 index 000000000000..80efcf665478 --- /dev/null +++ b/modules/zip/zip_packer.cpp @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* zip_packer.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "core/io/zip_io.h" + +#include "zip_packer.h" + +Error ZIPPacker::open(String path, ZipAppend append) { + if (f) { + close(); + } + + zlib_filefunc_def io = zipio_create_io_from_file(&f); + zf = zipOpen2(path.utf8().get_data(), append, NULL, &io); + return zf != NULL ? OK : FAILED; +} + +Error ZIPPacker::close() { + ERR_FAIL_COND_V_MSG(!f, FAILED, "ZIPPacker cannot be closed because it is not open."); + + return zipClose(zf, NULL) == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::start_file(String path) { + ERR_FAIL_COND_V_MSG(!f, FAILED, "ZIPPacker must be opened before use."); + + zip_fileinfo zipfi; + + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.min; + zipfi.tmz_date.tm_mon = date.month - 1; + zipfi.tmz_date.tm_sec = time.sec; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + zipfi.external_fa = 0; + zipfi.internal_fa = 0; + + int ret = zipOpenNewFileInZip(zf, path.utf8().get_data(), &zipfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + return ret == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::write_file(Vector data) { + ERR_FAIL_COND_V_MSG(!f, FAILED, "ZIPPacker must be opened before use."); + + return zipWriteInFileInZip(zf, data.ptr(), data.size()) == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::close_file() { + ERR_FAIL_COND_V_MSG(!f, FAILED, "ZIPPacker must be opened before use."); + + return zipCloseFileInZip(zf) == ZIP_OK ? OK : FAILED; +} + +void ZIPPacker::_bind_methods() { + ClassDB::bind_method(D_METHOD("open", "path", "append"), &ZIPPacker::open, DEFVAL(Variant(APPEND_CREATE))); + ClassDB::bind_method(D_METHOD("start_file", "path"), &ZIPPacker::start_file); + ClassDB::bind_method(D_METHOD("write_file", "data"), &ZIPPacker::write_file); + ClassDB::bind_method(D_METHOD("close_file"), &ZIPPacker::close_file); + ClassDB::bind_method(D_METHOD("close"), &ZIPPacker::close); + + BIND_ENUM_CONSTANT(APPEND_CREATE); + BIND_ENUM_CONSTANT(APPEND_CREATEAFTER); + BIND_ENUM_CONSTANT(APPEND_ADDINZIP); +} + +ZIPPacker::ZIPPacker() { + f = NULL; +} + +ZIPPacker::~ZIPPacker() { + if (f) { + close(); + } +} diff --git a/modules/zip/zip_packer.h b/modules/zip/zip_packer.h new file mode 100644 index 000000000000..8ee92f1c61df --- /dev/null +++ b/modules/zip/zip_packer.h @@ -0,0 +1,69 @@ +/**************************************************************************/ +/* zip_packer.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef ZIP_PACKER_H +#define ZIP_PACKER_H + +#include "core/reference.h" + +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "thirdparty/minizip/zip.h" + +class ZIPPacker : public Reference { + GDCLASS(ZIPPacker, Object); + + FileAccess *f; + zipFile zf; + +protected: + static void _bind_methods(); + +public: + enum ZipAppend { + APPEND_CREATE = 0, + APPEND_CREATEAFTER = 1, + APPEND_ADDINZIP = 2, + }; + + Error open(String path, ZipAppend append); + Error close(); + + Error start_file(String path); + Error write_file(Vector data); + Error close_file(); + + ZIPPacker(); + ~ZIPPacker(); +}; + +VARIANT_ENUM_CAST(ZIPPacker::ZipAppend) + +#endif // ZIP_PACKER_H diff --git a/modules/zip/zip_reader.cpp b/modules/zip/zip_reader.cpp new file mode 100644 index 000000000000..35985fddff72 --- /dev/null +++ b/modules/zip/zip_reader.cpp @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* zip_reader.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "zip_reader.h" + +#include "core/error_macros.h" +#include "core/io/zip_io.h" + +Error ZIPReader::open(String path) { + if (f) { + close(); + } + + zlib_filefunc_def io = zipio_create_io_from_file(&f); + uzf = unzOpen2(path.utf8().get_data(), &io); + return uzf != NULL ? OK : FAILED; +} + +Error ZIPReader::close() { + ERR_FAIL_COND_V_MSG(!f, FAILED, "ZIPReader cannot be closed because it is not open."); + + return unzClose(uzf) == UNZ_OK ? OK : FAILED; +} + +PoolStringArray ZIPReader::get_files() { + ERR_FAIL_COND_V_MSG(!f, PoolStringArray(), "ZIPReader must be opened before use."); + + List s; + + if (unzGoToFirstFile(uzf) != UNZ_OK) { + return PoolStringArray(); + } + + do { + char filename[256]; + unzGetCurrentFileInfo(uzf, NULL, filename, sizeof(filename), NULL, 0, NULL, 0); + s.push_back(filename); + } while (unzGoToNextFile(uzf) == UNZ_OK); + + PoolStringArray arr; + arr.resize(s.size()); + int idx = 0; + for (const List::Element *E = s.front(); E; E = E->next()) { + arr.set(idx++, E->get()); + } + return arr; +} + +PoolByteArray ZIPReader::read_file(String path, bool case_sensitive) { + ERR_FAIL_COND_V_MSG(!f, PoolByteArray(), "ZIPReader must be opened before use."); + + int cs = case_sensitive ? 1 : 2; + if (unzLocateFile(uzf, path.utf8().get_data(), cs) != UNZ_OK) { + ERR_FAIL_V_MSG(PoolByteArray(), "File does not exist in zip archive: " + path); + } + if (unzOpenCurrentFile(uzf) != UNZ_OK) { + ERR_FAIL_V_MSG(PoolByteArray(), "Could not open file within zip archive."); + } + + unz_file_info info; + unzGetCurrentFileInfo(uzf, &info, NULL, 0, NULL, 0, NULL, 0); + PoolByteArray data; + data.resize(info.uncompressed_size); + + PoolByteArray::Write w = data.write(); + unzReadCurrentFile(uzf, &w[0], info.uncompressed_size); + w.release(); + + unzCloseCurrentFile(uzf); + return data; +} + +ZIPReader::ZIPReader() { + f = NULL; +} + +ZIPReader::~ZIPReader() { + if (f) { + close(); + } +} + +void ZIPReader::_bind_methods() { + ClassDB::bind_method(D_METHOD("open", "path"), &ZIPReader::open); + ClassDB::bind_method(D_METHOD("close"), &ZIPReader::close); + ClassDB::bind_method(D_METHOD("get_files"), &ZIPReader::get_files); + ClassDB::bind_method(D_METHOD("read_file", "path", "case_sensitive"), &ZIPReader::read_file, DEFVAL(Variant(true))); +} diff --git a/modules/zip/zip_reader.h b/modules/zip/zip_reader.h new file mode 100644 index 000000000000..e9b7f8e3e9d1 --- /dev/null +++ b/modules/zip/zip_reader.h @@ -0,0 +1,60 @@ +/**************************************************************************/ +/* zip_reader.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef ZIP_READER_H +#define ZIP_READER_H + +#include "core/reference.h" + +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "thirdparty/minizip/unzip.h" + +class ZIPReader : public Reference { + GDCLASS(ZIPReader, Object) + + FileAccess *f; + unzFile uzf; + +protected: + static void _bind_methods(); + +public: + Error open(String path); + Error close(); + + PoolStringArray get_files(); + PoolByteArray read_file(String path, bool case_sensitive); + + ZIPReader(); + ~ZIPReader(); +}; + +#endif // ZIP_READER_H