diff --git a/src/internal/CMakeLists.txt b/src/internal/CMakeLists.txt index 153d6b8c..6f69dad5 100644 --- a/src/internal/CMakeLists.txt +++ b/src/internal/CMakeLists.txt @@ -4,4 +4,6 @@ target_sources(scratchcpp scratch3reader.cpp scratch3reader.h reader_common.h + zipreader.cpp + zipreader.h ) diff --git a/src/internal/iprojectreader.h b/src/internal/iprojectreader.h index 4148d9b4..00cee24c 100644 --- a/src/internal/iprojectreader.h +++ b/src/internal/iprojectreader.h @@ -34,6 +34,7 @@ class IProjectReader virtual std::vector extensions() = 0; protected: + virtual void printErr(const std::string &errStr) final { std::cerr << "Failed to read project: " << errStr << std::endl; } virtual void printErr(const std::string &errStr, const char *what) final { std::cerr << "Failed to read project: " << errStr << std::endl << what << std::endl; } private: diff --git a/src/internal/scratch3reader.cpp b/src/internal/scratch3reader.cpp index ee125990..2223c8b0 100644 --- a/src/internal/scratch3reader.cpp +++ b/src/internal/scratch3reader.cpp @@ -14,6 +14,7 @@ #include "scratch3reader.h" #include "reader_common.h" +#include "zipreader.h" using namespace libscratchcpp; using json = nlohmann::json; @@ -299,14 +300,9 @@ bool Scratch3Reader::load() else printErr(std::string("could not parse ") + step, e.what()); - // Close the zip file - zip_close(m_zip); - return false; } - // Close the zip file - zip_close(m_zip); return true; } @@ -352,37 +348,15 @@ std::vector Scratch3Reader::extensions() void Scratch3Reader::read() { - // Open the zip file - unsigned char *buf; - size_t bufsize; - m_zip = zip_open(fileName().c_str(), 0, 'r'); - if (!m_zip) { - std::cerr << "Failed to open project file" << std::endl; - return; - } - - // Extract project.json - zip_entry_open(m_zip, "project.json"); - bufsize = zip_entry_size(m_zip); - buf = (unsigned char *)calloc(sizeof(unsigned char), bufsize); - zip_entry_noallocread(m_zip, (void *)buf, bufsize); - zip_entry_close(m_zip); - std::string str(reinterpret_cast(buf)); - free(buf); - - // Remove garbage after the JSON - int end; - for (end = str.size(); end >= 0; end--) { - char ch = str[end]; - if (ch == '}') - break; - } - std::string out = str.substr(0, end + 1); - - // Parse the JSON - try { - m_json = json::parse(out); - } catch (std::exception &e) { - printErr("invalid JSON file", e.what()); - } + // Read project.json + ZipReader reader(fileName()); + if (reader.open()) { + // Parse the JSON + try { + m_json = json::parse(reader.readFileToString("project.json")); + } catch (std::exception &e) { + printErr("invalid JSON file", e.what()); + } + } else + printErr("could not read " + fileName()); } diff --git a/src/internal/scratch3reader.h b/src/internal/scratch3reader.h index 752146aa..528895b4 100644 --- a/src/internal/scratch3reader.h +++ b/src/internal/scratch3reader.h @@ -4,7 +4,6 @@ #include "iprojectreader.h" #include -#include namespace libscratchcpp { @@ -21,7 +20,6 @@ class Scratch3Reader : public IProjectReader private: void read(); - struct zip_t *m_zip = nullptr; nlohmann::json m_json = ""; std::vector> m_targets; std::vector> m_broadcasts; diff --git a/src/internal/zipreader.cpp b/src/internal/zipreader.cpp new file mode 100644 index 00000000..ae0358b0 --- /dev/null +++ b/src/internal/zipreader.cpp @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "zipreader.h" + +using namespace libscratchcpp; + +ZipReader::ZipReader(const std::string &fileName) : + m_fileName(fileName) +{ +} + +ZipReader::ZipReader(const char *fileName) : + ZipReader(std::string(fileName)) +{ +} + +ZipReader::~ZipReader() +{ + close(); +} + +bool ZipReader::open() +{ + m_zip = zip_open(m_fileName.c_str(), 0, 'r'); + return m_zip; +} + +void ZipReader::close() +{ + if (m_zip) + zip_close(m_zip); + + m_zip = nullptr; +} + +size_t ZipReader::readFile(const std::string &fileName, void **buf) +{ + if (!m_zip) { + buf = nullptr; + return 0; + } + + size_t bufsize = 0; + zip_entry_open(m_zip, fileName.c_str()); + zip_entry_read(m_zip, buf, &bufsize); + zip_entry_close(m_zip); + + return bufsize; +} + +std::string ZipReader::readFileToString(const std::string &fileName) +{ + void *buf = nullptr; + size_t bufsize = readFile(fileName, &buf); + + if (buf) { + std::string ret(reinterpret_cast(buf), bufsize); + free(buf); + + return ret; + } + + return ""; +} diff --git a/src/internal/zipreader.h b/src/internal/zipreader.h new file mode 100644 index 00000000..9c9db512 --- /dev/null +++ b/src/internal/zipreader.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +namespace libscratchcpp +{ + +class ZipReader +{ + public: + ZipReader(const std::string &fileName); + ZipReader(const char *fileName); + ZipReader(const ZipReader &) = delete; + ~ZipReader(); + + bool open(); + void close(); + + size_t readFile(const std::string &fileName, void **buf); + std::string readFileToString(const std::string &fileName); + + private: + std::string m_fileName; + struct zip_t *m_zip = nullptr; +}; + +} // namespace libscratchcpp diff --git a/test/zip/CMakeLists.txt b/test/zip/CMakeLists.txt index d659998e..4026dd84 100644 --- a/test/zip/CMakeLists.txt +++ b/test/zip/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable( target_link_libraries( zip_test + scratchcpp GTest::gtest_main zip ) diff --git a/test/zip/zip_test.cpp b/test/zip/zip_test.cpp index c5c13372..666cfae7 100644 --- a/test/zip/zip_test.cpp +++ b/test/zip/zip_test.cpp @@ -1,34 +1,19 @@ -#include -#include -#include +#include #include "../common.h" -std::string readSb3Json(const std::string &fileName) +using namespace libscratchcpp; + +inline std::string readSb3Json(const std::string &fileName) +{ + ZipReader reader(fileName); + reader.open(); + + return reader.readFileToString("project.json"); +} + +TEST(ZipTest, NonexistentProject) { - // TODO: Move this to a class and use it in Scratch3Reader - // Open the zip file - unsigned char *buf; - size_t bufsize; - struct zip_t *zip = zip_open(fileName.c_str(), 0, 'r'); - - // Extract project.json - zip_entry_open(zip, "project.json"); - bufsize = zip_entry_size(zip); - buf = (unsigned char *)calloc(sizeof(unsigned char), bufsize); - zip_entry_noallocread(zip, (void *)buf, bufsize); - zip_entry_close(zip); - std::string str(reinterpret_cast(buf)); - free(buf); - - // Remove garbage after the JSON - int end; - for (end = str.size(); end >= 0; end--) { - char ch = str[end]; - if (ch == '}') { - break; - } - } - return str.substr(0, end + 1); + ASSERT_TRUE(readSb3Json("idontexist.sb3").empty()); } TEST(ZipTest, EmptyProject)