Skip to content

Commit b926efe

Browse files
authored
Merge pull request #175 from scratchcpp/zip_api
Add internal API for reading ZIP files
2 parents 8ef05d4 + 09b28aa commit b926efe

File tree

8 files changed

+123
-68
lines changed

8 files changed

+123
-68
lines changed

src/internal/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ target_sources(scratchcpp
44
scratch3reader.cpp
55
scratch3reader.h
66
reader_common.h
7+
zipreader.cpp
8+
zipreader.h
79
)

src/internal/iprojectreader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class IProjectReader
3434
virtual std::vector<std::string> extensions() = 0;
3535

3636
protected:
37+
virtual void printErr(const std::string &errStr) final { std::cerr << "Failed to read project: " << errStr << std::endl; }
3738
virtual void printErr(const std::string &errStr, const char *what) final { std::cerr << "Failed to read project: " << errStr << std::endl << what << std::endl; }
3839

3940
private:

src/internal/scratch3reader.cpp

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "scratch3reader.h"
1616
#include "reader_common.h"
17+
#include "zipreader.h"
1718

1819
using namespace libscratchcpp;
1920
using json = nlohmann::json;
@@ -299,14 +300,9 @@ bool Scratch3Reader::load()
299300
else
300301
printErr(std::string("could not parse ") + step, e.what());
301302

302-
// Close the zip file
303-
zip_close(m_zip);
304-
305303
return false;
306304
}
307305

308-
// Close the zip file
309-
zip_close(m_zip);
310306
return true;
311307
}
312308

@@ -352,37 +348,15 @@ std::vector<std::string> Scratch3Reader::extensions()
352348

353349
void Scratch3Reader::read()
354350
{
355-
// Open the zip file
356-
unsigned char *buf;
357-
size_t bufsize;
358-
m_zip = zip_open(fileName().c_str(), 0, 'r');
359-
if (!m_zip) {
360-
std::cerr << "Failed to open project file" << std::endl;
361-
return;
362-
}
363-
364-
// Extract project.json
365-
zip_entry_open(m_zip, "project.json");
366-
bufsize = zip_entry_size(m_zip);
367-
buf = (unsigned char *)calloc(sizeof(unsigned char), bufsize);
368-
zip_entry_noallocread(m_zip, (void *)buf, bufsize);
369-
zip_entry_close(m_zip);
370-
std::string str(reinterpret_cast<char const *>(buf));
371-
free(buf);
372-
373-
// Remove garbage after the JSON
374-
int end;
375-
for (end = str.size(); end >= 0; end--) {
376-
char ch = str[end];
377-
if (ch == '}')
378-
break;
379-
}
380-
std::string out = str.substr(0, end + 1);
381-
382-
// Parse the JSON
383-
try {
384-
m_json = json::parse(out);
385-
} catch (std::exception &e) {
386-
printErr("invalid JSON file", e.what());
387-
}
351+
// Read project.json
352+
ZipReader reader(fileName());
353+
if (reader.open()) {
354+
// Parse the JSON
355+
try {
356+
m_json = json::parse(reader.readFileToString("project.json"));
357+
} catch (std::exception &e) {
358+
printErr("invalid JSON file", e.what());
359+
}
360+
} else
361+
printErr("could not read " + fileName());
388362
}

src/internal/scratch3reader.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
#include "iprojectreader.h"
66
#include <nlohmann/json.hpp>
7-
#include <zip.h>
87

98
namespace libscratchcpp
109
{
@@ -21,7 +20,6 @@ class Scratch3Reader : public IProjectReader
2120

2221
private:
2322
void read();
24-
struct zip_t *m_zip = nullptr;
2523
nlohmann::json m_json = "";
2624
std::vector<std::shared_ptr<Target>> m_targets;
2725
std::vector<std::shared_ptr<Broadcast>> m_broadcasts;

src/internal/zipreader.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "zipreader.h"
4+
5+
using namespace libscratchcpp;
6+
7+
ZipReader::ZipReader(const std::string &fileName) :
8+
m_fileName(fileName)
9+
{
10+
}
11+
12+
ZipReader::ZipReader(const char *fileName) :
13+
ZipReader(std::string(fileName))
14+
{
15+
}
16+
17+
ZipReader::~ZipReader()
18+
{
19+
close();
20+
}
21+
22+
bool ZipReader::open()
23+
{
24+
m_zip = zip_open(m_fileName.c_str(), 0, 'r');
25+
return m_zip;
26+
}
27+
28+
void ZipReader::close()
29+
{
30+
if (m_zip)
31+
zip_close(m_zip);
32+
33+
m_zip = nullptr;
34+
}
35+
36+
size_t ZipReader::readFile(const std::string &fileName, void **buf)
37+
{
38+
if (!m_zip) {
39+
buf = nullptr;
40+
return 0;
41+
}
42+
43+
size_t bufsize = 0;
44+
zip_entry_open(m_zip, fileName.c_str());
45+
zip_entry_read(m_zip, buf, &bufsize);
46+
zip_entry_close(m_zip);
47+
48+
return bufsize;
49+
}
50+
51+
std::string ZipReader::readFileToString(const std::string &fileName)
52+
{
53+
void *buf = nullptr;
54+
size_t bufsize = readFile(fileName, &buf);
55+
56+
if (buf) {
57+
std::string ret(reinterpret_cast<char *>(buf), bufsize);
58+
free(buf);
59+
60+
return ret;
61+
}
62+
63+
return "";
64+
}

src/internal/zipreader.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <string>
6+
#include <zip.h>
7+
8+
namespace libscratchcpp
9+
{
10+
11+
class ZipReader
12+
{
13+
public:
14+
ZipReader(const std::string &fileName);
15+
ZipReader(const char *fileName);
16+
ZipReader(const ZipReader &) = delete;
17+
~ZipReader();
18+
19+
bool open();
20+
void close();
21+
22+
size_t readFile(const std::string &fileName, void **buf);
23+
std::string readFileToString(const std::string &fileName);
24+
25+
private:
26+
std::string m_fileName;
27+
struct zip_t *m_zip = nullptr;
28+
};
29+
30+
} // namespace libscratchcpp

test/zip/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_executable(
55

66
target_link_libraries(
77
zip_test
8+
scratchcpp
89
GTest::gtest_main
910
zip
1011
)

test/zip/zip_test.cpp

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,19 @@
1-
#include <gtest/gtest.h>
2-
#include <zip.h>
3-
#include <filesystem>
1+
#include <internal/zipreader.h>
42
#include "../common.h"
53

6-
std::string readSb3Json(const std::string &fileName)
4+
using namespace libscratchcpp;
5+
6+
inline std::string readSb3Json(const std::string &fileName)
7+
{
8+
ZipReader reader(fileName);
9+
reader.open();
10+
11+
return reader.readFileToString("project.json");
12+
}
13+
14+
TEST(ZipTest, NonexistentProject)
715
{
8-
// TODO: Move this to a class and use it in Scratch3Reader
9-
// Open the zip file
10-
unsigned char *buf;
11-
size_t bufsize;
12-
struct zip_t *zip = zip_open(fileName.c_str(), 0, 'r');
13-
14-
// Extract project.json
15-
zip_entry_open(zip, "project.json");
16-
bufsize = zip_entry_size(zip);
17-
buf = (unsigned char *)calloc(sizeof(unsigned char), bufsize);
18-
zip_entry_noallocread(zip, (void *)buf, bufsize);
19-
zip_entry_close(zip);
20-
std::string str(reinterpret_cast<char const *>(buf));
21-
free(buf);
22-
23-
// Remove garbage after the JSON
24-
int end;
25-
for (end = str.size(); end >= 0; end--) {
26-
char ch = str[end];
27-
if (ch == '}') {
28-
break;
29-
}
30-
}
31-
return str.substr(0, end + 1);
16+
ASSERT_TRUE(readSb3Json("idontexist.sb3").empty());
3217
}
3318

3419
TEST(ZipTest, EmptyProject)

0 commit comments

Comments
 (0)