Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/internal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ target_sources(scratchcpp
scratch3reader.cpp
scratch3reader.h
reader_common.h
zipreader.cpp
zipreader.h
)
1 change: 1 addition & 0 deletions src/internal/iprojectreader.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class IProjectReader
virtual std::vector<std::string> 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:
Expand Down
50 changes: 12 additions & 38 deletions src/internal/scratch3reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "scratch3reader.h"
#include "reader_common.h"
#include "zipreader.h"

using namespace libscratchcpp;
using json = nlohmann::json;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -352,37 +348,15 @@ std::vector<std::string> 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<char const *>(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());
}
2 changes: 0 additions & 2 deletions src/internal/scratch3reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include "iprojectreader.h"
#include <nlohmann/json.hpp>
#include <zip.h>

namespace libscratchcpp
{
Expand All @@ -21,7 +20,6 @@ class Scratch3Reader : public IProjectReader

private:
void read();
struct zip_t *m_zip = nullptr;
nlohmann::json m_json = "";
std::vector<std::shared_ptr<Target>> m_targets;
std::vector<std::shared_ptr<Broadcast>> m_broadcasts;
Expand Down
64 changes: 64 additions & 0 deletions src/internal/zipreader.cpp
Original file line number Diff line number Diff line change
@@ -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<char *>(buf), bufsize);
free(buf);

return ret;
}

return "";
}
30 changes: 30 additions & 0 deletions src/internal/zipreader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <string>
#include <zip.h>

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
1 change: 1 addition & 0 deletions test/zip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_executable(

target_link_libraries(
zip_test
scratchcpp
GTest::gtest_main
zip
)
Expand Down
41 changes: 13 additions & 28 deletions test/zip/zip_test.cpp
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
#include <gtest/gtest.h>
#include <zip.h>
#include <filesystem>
#include <internal/zipreader.h>
#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<char const *>(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)
Expand Down