open-story-teller/story-editor/ost_convert/zip.cpp
2023-04-18 16:20:41 +02:00

275 lines
7.5 KiB
C++

/**
* MIT License
* Copyright (c) 2019 Anthony Rabine
*/
#include "zip.h"
#include <fstream>
#include <cstring>
#include <functional>
#include <filesystem>
#include <memory>
static bool ensure_file_exists_and_is_readable(const char *pFilename)
{
FILE *p = nullptr;
#ifdef Q_OS_WIN
fopen_s(& p, pFilename, "rb");
#else
p = fopen(pFilename, "rb");
#endif
if (!p) {
return false;
}
fseek(p, 0, SEEK_END);
long int src_file_size = ftell(p);
fseek(p, 0, SEEK_SET);
if (src_file_size)
{
char buf[1];
if (fread(buf, 1, 1, p) != 1)
{
fclose(p);
return false;
}
}
fclose(p);
return true;
}
/*****************************************************************************/
Zip::Zip()
: mIsValid(false)
, mNumberOfEntries(0U)
{
std::memset(&mZipArchive, 0, sizeof(mZipArchive));
}
/*****************************************************************************/
Zip::~Zip()
{
Close();
}
/*****************************************************************************/
void Zip::CreateInMemory(const std::string &fileName)
{
mz_bool status = MZ_FALSE;
std::memset(&mZipArchive, 0, sizeof(mZipArchive));
status = mz_zip_writer_init_file(&mZipArchive, fileName.c_str(), 65537);
if (status)
{
mIsOpenForWriting = true;
mIsValid = true;
mIsOpen = true;
}
else
{
mIsOpenForWriting = false;
mIsValid = false;
}
}
/*****************************************************************************/
bool Zip::AddFile(const std::string &fileName, const std::string &archiveName)
{
mz_bool status = MZ_FALSE;
if (ensure_file_exists_and_is_readable(fileName.c_str()))
{
status = mz_zip_writer_add_file(&mZipArchive, archiveName.c_str(), fileName.c_str(), NULL, 0, MZ_NO_COMPRESSION);
}
return status == MZ_TRUE;
}
/*****************************************************************************/
void Zip::AddDirectory(const std::string &dirName)
{
std::string d = dirName + "/";
mz_zip_writer_add_mem(&mZipArchive, d.c_str(), NULL, 0, MZ_NO_COMPRESSION);
}
/*****************************************************************************/
std::vector<std::string> Zip::Unzip(std::string const &zipFile, const std::string &destination_dir, std::string const &password)
{
(void)password;
std::vector<std::string> files = {};
mz_zip_archive zip_archive;
memset(&zip_archive, 0, sizeof(zip_archive));
auto status = mz_zip_reader_init_file(&zip_archive, zipFile.c_str(), 0);
if (!status) return files;
int fileCount = (int)mz_zip_reader_get_num_files(&zip_archive);
if (fileCount == 0)
{
mz_zip_reader_end(&zip_archive);
return files;
}
mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip_archive, 0, &file_stat))
{
mz_zip_reader_end(&zip_archive);
return files;
}
// Get root folder
// std::string baseName = std::filesystem::path(file_stat.m_filename).parent_path().string();
// Get and print information about each file in the archive.
for (int i = 0; i < fileCount; i++)
{
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue;
if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) continue; // skip directories for now
std::string fileName = file_stat.m_filename; // make path relative
// std::string destFile = destination_dir + std::filesystem::path::preferred_separator + fileName; // make full dest path
// creates the directory where the file will be decompressed
std::filesystem::create_directories(std::filesystem::path(fileName).parent_path());
// Extract file
if (mz_zip_reader_extract_to_file(&zip_archive, i, fileName.data(), 0))
{
files.emplace_back(fileName);
}
}
// Close the archive, freeing any resources it was using
mz_zip_reader_end(&zip_archive);
return files;
}
/*****************************************************************************/
bool Zip::Open(const std::string &zip, bool isFile)
{
mz_bool status;
mIsValid = false;
mIsOpenForWriting = true;
mNumberOfEntries = 0U;
std::memset(&mZipArchive, 0, sizeof(mZipArchive));
if (isFile)
{
// Physical file on disk
status = mz_zip_reader_init_file(&mZipArchive, zip.c_str(), 0);
}
else
{
// Zipped memory
status = mz_zip_reader_init_mem(&mZipArchive, zip.c_str(), zip.size(), 0);
}
if (status)
{
mFiles.clear();
// Get and print information about each file in the archive.
for (std::uint32_t i = 0; i < mz_zip_reader_get_num_files(&mZipArchive); i++)
{
mz_zip_archive_file_stat file_stat;
if (mz_zip_reader_file_stat(&mZipArchive, i, &file_stat))
{
mNumberOfEntries++;
if (file_stat.m_is_directory == MZ_FALSE)
{
mFiles.push_back(file_stat.m_filename);
//printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u\n", file_stat.m_filename, file_stat.m_comment, (std::uint32_t)file_stat.m_uncomp_size, (std::uint32_t)file_stat.m_comp_size);
}
}
}
}
if (mNumberOfEntries == mz_zip_reader_get_num_files(&mZipArchive))
{
mIsValid = true;
mIsOpen = true;
}
return mIsValid;
}
/*****************************************************************************/
void Zip::Close()
{
if (mIsValid)
{
if (mIsOpenForWriting)
{
if (!mz_zip_writer_finalize_archive(&mZipArchive))
{
mz_zip_writer_end(&mZipArchive);
}
}
else
{
mz_zip_reader_end(&mZipArchive);
}
}
mIsOpen = false;
}
/*****************************************************************************/
struct UserData
{
char *output;
int offset;
};
static mz_bool DeflateCallback(const void *pBuf, int len, void *pUser)
{
UserData *ud = static_cast<UserData*>(pUser);
std::memcpy(ud->output + ud->offset, pBuf, len);
ud->offset += len;
(void) len;
(void) pUser;
return MZ_TRUE;
}
/*****************************************************************************/
int Zip::CompressBuffer(const char *input, size_t input_size, char *output)
{
int finalsize = -1;
tdefl_compressor Comp;
UserData ud;
ud.offset = 0U;
ud.output = output;
if (tdefl_init(&Comp, DeflateCallback, &ud, 0) == TDEFL_STATUS_OKAY)
{
if(tdefl_compress_buffer(&Comp, input, input_size, TDEFL_FINISH) == TDEFL_STATUS_DONE)
{
finalsize = ud.offset;
}
}
return finalsize;
}
/*****************************************************************************/
bool Zip::GetFile(const std::string &fileName, std::string &contents)
{
bool ret = false;
if (mIsValid)
{
size_t size;
char *p = reinterpret_cast<char *>(mz_zip_reader_extract_file_to_heap(&mZipArchive, fileName.c_str(), &size, 0));
if (p != nullptr)
{
contents.assign(p, size);
free(p);
ret = true;
}
}
return ret;
}
/*****************************************************************************/
std::vector<std::string> Zip::ListFiles()
{
return mFiles;
}
//=============================================================================
// End of file Zip.cpp
//=============================================================================