mirror of
https://github.com/idolpx/libMeatloaf.git
synced 2025-12-05 20:28:50 -05:00
365 lines
11 KiB
C++
365 lines
11 KiB
C++
|
|
#include "archive_ml.h"
|
|
|
|
#include <archive.h>
|
|
#include <archive_entry.h>
|
|
|
|
#include "meat_io.h"
|
|
|
|
/* Returns pointer and size of next block of data from archive. */
|
|
// The read callback returns the number of bytes read, zero for end-of-file, or a negative failure code as above.
|
|
// It also returns a pointer to the block of data read.
|
|
// https://github.com/libarchive/libarchive/wiki/LibarchiveIO
|
|
//
|
|
// This callback is just a way to get bytes from srcStream into libarchive for processing
|
|
ssize_t cb_read(struct archive *a, void *userData, const void **buff)
|
|
{
|
|
ArchiveStreamData *streamData = (ArchiveStreamData *)userData;
|
|
// 1. we have to call srcStr.read(...)
|
|
ssize_t bc = streamData->srcStream->read(streamData->srcBuffer, ArchiveStream::buffSize);
|
|
//std::string dump((char*)streamData->srcBuffer, bc);
|
|
//Debug_printv("Libarch pulling data from src MStream, got bytes:%d", bc);
|
|
//Debug_printv("Dumping bytes: %s", dump.c_str());
|
|
// 2. set *buff to the bufer read in 1.
|
|
*buff = streamData->srcBuffer;
|
|
// 3. return read bytes count
|
|
return bc;
|
|
}
|
|
|
|
/*
|
|
It must return the number of bytes actually skipped, or a negative failure code if skipping cannot be done.
|
|
It can skip fewer bytes than requested but must never skip more.
|
|
Only positive/forward skips will ever be requested.
|
|
If skipping is not provided or fails, libarchive will call the read() function and simply ignore any data that it does not need.
|
|
|
|
* Skips at most request bytes from archive and returns the skipped amount.
|
|
* This may skip fewer bytes than requested; it may even skip zero bytes.
|
|
* If you do skip fewer bytes than requested, libarchive will invoke your
|
|
* read callback and discard data as necessary to make up the full skip.
|
|
*/
|
|
// https://github.com/libarchive/libarchive/wiki/LibarchiveIO
|
|
int64_t cb_skip(struct archive *a, void *userData, int64_t request)
|
|
{
|
|
Debug_printv("bytes[%d]", request);
|
|
ArchiveStreamData *streamData = (ArchiveStreamData *)userData;
|
|
|
|
if (streamData->srcStream->isOpen())
|
|
{
|
|
bool rc = streamData->srcStream->seek(request, SEEK_CUR);
|
|
return (rc) ? request : ARCHIVE_WARN;
|
|
}
|
|
else
|
|
{
|
|
Debug_printv("ERROR! skip failed");
|
|
return ARCHIVE_FATAL;
|
|
}
|
|
}
|
|
|
|
int64_t cb_seek(struct archive *a, void *userData, int64_t offset, int whence)
|
|
{
|
|
Debug_printv("offset[%d] whence[%d] (0=begin, 1=curr, 2=end)", offset, whence);
|
|
ArchiveStreamData *streamData = (ArchiveStreamData *)userData;
|
|
|
|
if (streamData->srcStream->isOpen())
|
|
{
|
|
bool rc = streamData->srcStream->seek(offset, whence);
|
|
return (rc) ? offset : ARCHIVE_WARN;
|
|
}
|
|
else
|
|
{
|
|
Debug_printv("ERROR! seek failed");
|
|
return ARCHIVE_FATAL;
|
|
}
|
|
}
|
|
|
|
int cb_close(struct archive *a, void *userData)
|
|
{
|
|
ArchiveStreamData *src_str = (ArchiveStreamData *)userData;
|
|
|
|
Debug_printv("Libarch wants to close, but we do nothing here...");
|
|
|
|
// do we want to close srcStream here???
|
|
return (ARCHIVE_OK);
|
|
}
|
|
|
|
int cb_open(struct a *arch, void *userData)
|
|
{
|
|
// maybe we can use open for something? Check if stream is open?
|
|
return (ARCHIVE_OK);
|
|
}
|
|
|
|
|
|
/********************************************************
|
|
* Streams implementations
|
|
********************************************************/
|
|
|
|
ArchiveStream::ArchiveStream(std::shared_ptr<MStream> srcStr)
|
|
{
|
|
// it should be possible to to pass a password parameter here and somehow
|
|
// call archive_passphrase_callback(password) from here, right?
|
|
streamData.srcStream = srcStr;
|
|
a = archive_read_new();
|
|
archive_read_support_filter_all(a);
|
|
archive_read_support_format_all(a);
|
|
streamData.srcBuffer = new uint8_t[buffSize];
|
|
open();
|
|
}
|
|
|
|
ArchiveStream::~ArchiveStream()
|
|
{
|
|
close();
|
|
if (streamData.srcBuffer != nullptr)
|
|
delete[] streamData.srcBuffer;
|
|
Debug_printv("Stream destructor OK!");
|
|
}
|
|
|
|
bool ArchiveStream::open()
|
|
{
|
|
if (!is_open)
|
|
{
|
|
// TODO enable seek only if the stream is random access
|
|
archive_read_set_read_callback(a, cb_read);
|
|
archive_read_set_skip_callback(a, cb_skip);
|
|
archive_read_set_seek_callback(a, cb_seek);
|
|
archive_read_set_close_callback(a, cb_close);
|
|
// archive_read_set_open_callback(mpa->arch, cb_open); - what does it do?
|
|
archive_read_set_callback_data(a, &streamData);
|
|
Debug_printv("== BEGIN Calling open1 on archive instance ==========================");
|
|
int r = archive_read_open1(a);
|
|
Debug_printv("== END opening archive result=%d! (OK should be 0!) =======================================", r);
|
|
|
|
//int r = archive_read_open2(a, &streamData, NULL, myRead, myskip, myclose);
|
|
if (r == ARCHIVE_OK)
|
|
is_open = true;
|
|
}
|
|
return is_open;
|
|
};
|
|
|
|
void ArchiveStream::close()
|
|
{
|
|
if (is_open)
|
|
{
|
|
archive_read_close(a);
|
|
archive_read_free(a);
|
|
is_open = false;
|
|
}
|
|
Debug_printv("Close called");
|
|
}
|
|
|
|
bool ArchiveStream::isOpen()
|
|
{
|
|
return is_open;
|
|
};
|
|
|
|
std::vector<uint8_t> leftovers;
|
|
|
|
uint32_t ArchiveStream::read(uint8_t *buf, uint32_t size)
|
|
{
|
|
Debug_printv("calling read size[%d]", size);
|
|
const void *incomingBuffer;
|
|
size_t incomingSize;
|
|
int64_t offset;
|
|
|
|
int r = archive_read_data_block(a, &incomingBuffer, &incomingSize, &offset);
|
|
Debug_printv("r[%d]", r);
|
|
if ( r == ARCHIVE_EOF )
|
|
return 0;
|
|
|
|
if ( r < ARCHIVE_OK )
|
|
return 0;
|
|
|
|
// 'buff' contains the data of the current block
|
|
// 'size' is the size of the current block
|
|
|
|
std::vector<uint8_t> incomingVector((uint8_t*)incomingBuffer, (uint8_t*)incomingBuffer + incomingSize);
|
|
// concatenate intermediate buffer with incomingVector
|
|
leftovers.insert(leftovers.end(), incomingVector.begin(), incomingVector.end());
|
|
|
|
if(leftovers.size() <= size) {
|
|
// ok, we can fit everything that was left and new data to our buffer
|
|
auto size = leftovers.size();
|
|
_position += size;
|
|
leftovers.clear();
|
|
}
|
|
else {
|
|
// ok, so we can only write up to size and we have to keep leftovers for next time
|
|
std::copy(leftovers.begin(), leftovers.begin() + size, buf);
|
|
std::vector<uint8_t> leftovers2(leftovers.begin() + size, leftovers.end());
|
|
leftovers = leftovers2;
|
|
_position += size;
|
|
}
|
|
_position += size;
|
|
|
|
Debug_printv("size[%d] position[%d]", size, _position);
|
|
|
|
return size;
|
|
}
|
|
|
|
uint32_t ArchiveStream::write(const uint8_t *buf, uint32_t size)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// For files with a browsable random access directory structure
|
|
// d64, d74, d81, dnp, etc.
|
|
bool ArchiveStream::seekPath(std::string path)
|
|
{
|
|
Debug_printv("seekPath called for path: %s", path.c_str());
|
|
|
|
if ( seekEntry( path ) )
|
|
{
|
|
Debug_printv("entry[%s]", archive_entry_pathname(entry));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ArchiveStream::seekEntry( std::string filename )
|
|
{
|
|
Debug_printv( "filename[%s] size[%d]", filename.c_str(), filename.size());
|
|
|
|
// Read Directory Entries
|
|
if ( filename.size() > 0 )
|
|
{
|
|
bool found = false;
|
|
bool wildcard = ( mstr::contains(filename, "*") || mstr::contains(filename, "?") );
|
|
while ( archive_read_next_header(a, &entry) == ARCHIVE_OK )
|
|
{
|
|
std::string entryPath = ""; //archive_entry_sourcepath(entry);
|
|
std::string entryFilename = archive_entry_pathname(entry);
|
|
|
|
Debug_printv("path[%s] filename[%s] entry.filename[%.16s]", entryPath.c_str(), filename.c_str(), entryFilename.c_str());
|
|
|
|
// Check filetype
|
|
if ( archive_entry_filetype(entry) != AE_IFDIR )
|
|
{
|
|
// Read Entry From Stream
|
|
if (filename == "*") // Match first entry
|
|
{
|
|
filename = entryFilename;
|
|
found = true;
|
|
}
|
|
else if ( filename == entryFilename ) // Match exact
|
|
{
|
|
found = true;
|
|
}
|
|
else if ( wildcard )
|
|
{
|
|
if ( mstr::compare(filename, entryFilename) ) // X?XX?X* Wildcard match
|
|
{
|
|
// Set filename to this filename
|
|
Debug_printv( "Found! file[%s] -> entry[%s]", filename.c_str(), entryFilename.c_str() );
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if ( found )
|
|
{
|
|
_size = archive_entry_size(entry);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Debug_printv( "Not Found! file[%s]", filename.c_str() );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// // For files with no directory structure
|
|
// // tap, crt, tar
|
|
// std::string ArchiveStream::seekNextEntry()
|
|
// {
|
|
// struct archive_entry *entry;
|
|
// if (archive_read_next_header(a, &entry) == ARCHIVE_OK)
|
|
// return archive_entry_pathname(entry);
|
|
// else
|
|
// return "";
|
|
// };
|
|
|
|
|
|
bool ArchiveStream::seek(uint32_t pos)
|
|
{
|
|
return streamData.srcStream->seek(pos);
|
|
}
|
|
|
|
/********************************************************
|
|
* Files implementations
|
|
********************************************************/
|
|
|
|
MStream *ArchiveContainerFile::getDecodedStream(std::shared_ptr<MStream> containerIstream)
|
|
{
|
|
// TODO - we can get password from this URL and pass it as a parameter to this constructor
|
|
Debug_printv("calling getDecodedStream for ArchiveContainerFile, we should return open stream");
|
|
auto stream = new ArchiveStream(containerIstream);
|
|
return stream;
|
|
}
|
|
|
|
// archive file is always a directory
|
|
bool ArchiveContainerFile::isDirectory()
|
|
{
|
|
//Debug_printv("pathInStream[%s]", pathInStream.c_str());
|
|
if ( pathInStream == "" )
|
|
return true;
|
|
else
|
|
return false;
|
|
};
|
|
|
|
bool ArchiveContainerFile::rewindDirectory()
|
|
{
|
|
dirIsOpen = true;
|
|
|
|
return prepareDirListing();
|
|
}
|
|
|
|
MFile *ArchiveContainerFile::getNextFileInDir()
|
|
{
|
|
if(!dirIsOpen)
|
|
rewindDirectory();
|
|
|
|
struct archive_entry *entry;
|
|
|
|
Debug_printv("getNextFileInDir calling archive_read_next_header");
|
|
if (archive_read_next_header(getArchive(), &entry) == ARCHIVE_OK)
|
|
{
|
|
std::string fileName = archive_entry_pathname(entry);
|
|
auto file = MFSOwner::File(streamFile->url + "/" + fileName);
|
|
file->_size = archive_entry_size(entry);
|
|
file->_exists = true;
|
|
return file;
|
|
}
|
|
else
|
|
{
|
|
//Debug_printv( "END OF DIRECTORY");
|
|
dirStream->close();
|
|
dirIsOpen = false;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool ArchiveContainerFile::prepareDirListing()
|
|
{
|
|
if (dirStream.get() != nullptr)
|
|
{
|
|
dirStream->close();
|
|
}
|
|
|
|
Debug_printv("w prepare dir listing");
|
|
|
|
dirStream = std::shared_ptr<MStream>(this->getSourceStream());
|
|
|
|
if(dirStream->isOpen())
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Debug_printv("opening Archive for dir nok");
|
|
return false;
|
|
}
|
|
}
|