mirror of
https://github.com/idolpx/libMeatloaf.git
synced 2025-12-06 04:38:49 -05:00
602 lines
16 KiB
C++
602 lines
16 KiB
C++
#include "meat_io.h"
|
|
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <sstream>
|
|
|
|
#include "../../include/debug.h"
|
|
|
|
#include "MIOException.h"
|
|
|
|
#include "string_utils.h"
|
|
#include "peoples_url_parser.h"
|
|
|
|
//#include "wrappers/directory_stream.h"
|
|
|
|
// Archive
|
|
|
|
// Cartridge
|
|
|
|
// Container
|
|
#include "container/d8b.h"
|
|
#include "container/dfi.h"
|
|
|
|
// Device
|
|
#include "device/flash.h"
|
|
#include "device/sd.h"
|
|
|
|
// Disk
|
|
#include "disk/d64.h"
|
|
#include "disk/d71.h"
|
|
#include "disk/d80.h"
|
|
#include "disk/d81.h"
|
|
#include "disk/d82.h"
|
|
#include "disk/d90.h"
|
|
#include "disk/dnp.h"
|
|
|
|
// File
|
|
#include "file/p00.h"
|
|
#include "archive/archive_ml.h"
|
|
|
|
// Network
|
|
#include "network/http.h"
|
|
#include "network/tnfs.h"
|
|
// #include "network/ipfs.h"
|
|
// #include "network/tnfs.h"
|
|
// #include "network/smb.h"
|
|
// #include "network/ws.h"
|
|
|
|
// Service
|
|
// #include "service/cs.h"
|
|
#include "service/ml.h"
|
|
|
|
|
|
// Tape
|
|
#include "tape/t64.h"
|
|
#include "tape/tcrt.h"
|
|
|
|
#include "meat_buffer.h"
|
|
|
|
/********************************************************
|
|
* MFSOwner implementations
|
|
********************************************************/
|
|
|
|
// initialize other filesystems here
|
|
FlashFileSystem defaultFS;
|
|
#ifdef SD_CARD
|
|
SDFileSystem sdFS;
|
|
#endif
|
|
|
|
// Scheme
|
|
// HttpFileSystem httpFS;
|
|
// MLFileSystem mlFS;
|
|
// TNFSFileSystem tnfsFS;
|
|
// IPFSFileSystem ipfsFS;
|
|
// TNFSFileSystem tnfsFS;
|
|
// CServerFileSystem csFS;
|
|
// TcpFileSystem tcpFS;
|
|
|
|
//WSFileSystem wsFS;
|
|
|
|
// File
|
|
P00FileSystem p00FS;
|
|
//ArchiveContainerFileSystem archiveFS;
|
|
|
|
// Disk
|
|
D64FileSystem d64FS;
|
|
D71FileSystem d71FS;
|
|
D80FileSystem d80FS;
|
|
D81FileSystem d81FS;
|
|
D82FileSystem d82FS;
|
|
D90FileSystem d90FS;
|
|
DNPFileSystem dnpFS;
|
|
|
|
D8BFileSystem d8bFS;
|
|
DFIFileSystem dfiFS;
|
|
|
|
// Tape
|
|
T64FileSystem t64FS;
|
|
TCRTFileSystem tcrtFS;
|
|
|
|
// Cartridge
|
|
|
|
|
|
|
|
// put all available filesystems in this array - first matching system gets the file!
|
|
// fist in list is default
|
|
std::vector<MFileSystem*> MFSOwner::availableFS {
|
|
&defaultFS,
|
|
#ifdef SD_CARD
|
|
&sdFS,
|
|
#endif
|
|
// &archiveFS, // extension-based FS have to be on top to be picked first, otherwise the scheme will pick them!
|
|
&p00FS,
|
|
&d64FS, &d71FS, &d80FS, &d81FS, &d82FS, &d90FS, &dnpFS,
|
|
&d8bFS, &dfiFS,
|
|
&t64FS, &tcrtFS,
|
|
// &httpFS, &mlFS, &ipfsFS,
|
|
// &tnfsFS, &tcpFS,
|
|
// &tnfsFS
|
|
};
|
|
|
|
bool MFSOwner::mount(std::string name) {
|
|
Debug_print("MFSOwner::mount fs:");
|
|
Debug_println(name.c_str());
|
|
|
|
for(auto i = availableFS.begin() + 1; i < availableFS.end() ; i ++) {
|
|
auto fs = (*i);
|
|
|
|
if(fs->handles(name)) {
|
|
Debug_printv("MFSOwner found a proper fs");
|
|
|
|
bool ok = fs->mount();
|
|
|
|
if(ok)
|
|
Debug_print("Mounted fs: ");
|
|
else
|
|
Debug_print("Couldn't mount fs: ");
|
|
|
|
Debug_println(name.c_str());
|
|
|
|
return ok;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MFSOwner::umount(std::string name) {
|
|
for(auto i = availableFS.begin() + 1; i < availableFS.end() ; i ++) {
|
|
auto fs = (*i);
|
|
|
|
if(fs->handles(name)) {
|
|
return fs->umount();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
MFile* MFSOwner::File(MFile* file) {
|
|
return File(file->url);
|
|
}
|
|
|
|
MFile* MFSOwner::File(std::shared_ptr<MFile> file) {
|
|
return File(file->url);
|
|
}
|
|
|
|
|
|
MFile* MFSOwner::File(std::string path) {
|
|
//Debug_printv("in File\r\n");
|
|
|
|
// if(mstr::startsWith(path,"cs:", false)) {
|
|
// //Serial.printf("CServer path found!\r\n");
|
|
// return csFS.getFile(path);
|
|
// }
|
|
|
|
std::vector<std::string> paths = mstr::split(path,'/');
|
|
|
|
Debug_printv("Trying to factory path [%s]", path.c_str());
|
|
|
|
auto pathIterator = paths.end();
|
|
auto begin = paths.begin();
|
|
auto end = paths.end();
|
|
|
|
auto foundFS = testScan(begin, end, pathIterator);
|
|
|
|
if(foundFS != nullptr) {
|
|
Debug_printv("PATH: '%s' is in FS [%s]", path.c_str(), foundFS->symbol);
|
|
auto newFile = foundFS->getFile(path);
|
|
Debug_printv("newFile: '%s'", newFile->url.c_str());
|
|
|
|
pathIterator++;
|
|
newFile->pathInStream = mstr::joinToString(&pathIterator, &end, "/");
|
|
Debug_printv("newFile->pathInStream: '%s'", newFile->pathInStream.c_str());
|
|
|
|
auto endHere = pathIterator;
|
|
pathIterator--;
|
|
|
|
if(begin == pathIterator)
|
|
{
|
|
Debug_printv("** LOOK DOWN PATH NOT NEEDED path[%s]", path.c_str());
|
|
newFile->streamFile = foundFS->getFile(mstr::joinToString(&begin, &pathIterator, "/"));
|
|
}
|
|
else
|
|
{
|
|
auto upperPath = mstr::joinToString(&begin, &pathIterator, "/");
|
|
Debug_printv("** LOOK DOWN PATH: %s", upperPath.c_str());
|
|
|
|
auto upperFS = testScan(begin, end, pathIterator);
|
|
|
|
if(upperFS != nullptr) {
|
|
auto wholePath = mstr::joinToString(&begin, &endHere, "/");
|
|
|
|
//auto cp = mstr::joinToString(&begin, &pathIterator, "/");
|
|
Debug_printv("CONTAINER PATH WILL BE: '%s' ", wholePath.c_str());
|
|
newFile->streamFile = upperFS->getFile(wholePath); // skończy się na d64
|
|
Debug_printv("CONTAINER: '%s' is in FS [%s]", newFile->streamFile->url.c_str(), upperFS->symbol);
|
|
}
|
|
else {
|
|
Debug_printv("WARNING!!!! CONTAINER FAILED FOR: '%s'", upperPath.c_str());
|
|
}
|
|
}
|
|
|
|
return newFile;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::string MFSOwner::existsLocal( std::string path )
|
|
{
|
|
PeoplesUrlParser *url = PeoplesUrlParser::parseURL( path );
|
|
|
|
// Debug_printv( "path[%s] name[%s] size[%d]", path.c_str(), url.name.c_str(), url.name.size() );
|
|
if ( url->name.size() == 16 )
|
|
{
|
|
struct stat st;
|
|
int i = stat(std::string(path).c_str(), &st);
|
|
|
|
// If not found try for a wildcard match
|
|
if ( i == -1 )
|
|
{
|
|
DIR *dir;
|
|
struct dirent *ent;
|
|
|
|
std::string p = url->pathToFile();
|
|
std::string name = url->name;
|
|
// Debug_printv( "pathToFile[%s] basename[%s]", p.c_str(), name.c_str() );
|
|
if ((dir = opendir ( p.c_str() )) != NULL)
|
|
{
|
|
/* print all the files and directories within directory */
|
|
std::string e;
|
|
while ((ent = readdir (dir)) != NULL) {
|
|
// Debug_printv( "%s\r\n", ent->d_name );
|
|
e = ent->d_name;
|
|
if ( mstr::compare( name, e ) )
|
|
{
|
|
path = ( p + "/" + e );
|
|
break;
|
|
}
|
|
}
|
|
closedir (dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
MFileSystem* MFSOwner::testScan(std::vector<std::string>::iterator &begin, std::vector<std::string>::iterator &end, std::vector<std::string>::iterator &pathIterator) {
|
|
while (pathIterator != begin) {
|
|
pathIterator--;
|
|
|
|
auto part = *pathIterator;
|
|
mstr::toLower(part);
|
|
|
|
//Debug_printv("index[%d] pathIterator[%s] size[%d]", pathIterator, pathIterator->c_str(), pathIterator->size());
|
|
|
|
auto foundIter=std::find_if(availableFS.begin() + 1, availableFS.end(), [&part](MFileSystem* fs){
|
|
//Debug_printv("symbol[%s]", fs->symbol);
|
|
return fs->handles(part);
|
|
} );
|
|
|
|
if(foundIter != availableFS.end()) {
|
|
//Debug_printv("matched part '%s'\r\n", part.c_str());
|
|
return (*foundIter);
|
|
}
|
|
};
|
|
|
|
auto fs = *availableFS.begin();
|
|
//Debug_printv("return default file system [%s]", fs->symbol);
|
|
return fs;
|
|
}
|
|
|
|
/********************************************************
|
|
* MFileSystem implementations
|
|
********************************************************/
|
|
|
|
MFileSystem::MFileSystem(const char* s)
|
|
{
|
|
symbol = s;
|
|
}
|
|
|
|
MFileSystem::~MFileSystem() {}
|
|
|
|
/********************************************************
|
|
* MFile implementations
|
|
********************************************************/
|
|
|
|
MFile::MFile(std::string path) {
|
|
// Debug_printv("path[%s]", path.c_str());
|
|
|
|
// if ( mstr::contains(path, "$") )
|
|
// {
|
|
// // Create directory stream here
|
|
// Debug_printv("Create directory stream here!");
|
|
// path = "";
|
|
// }
|
|
|
|
resetURL(path);
|
|
}
|
|
|
|
MFile::MFile(std::string path, std::string name) : MFile(path + "/" + name) {
|
|
if(mstr::startsWith(name, "xn--")) {
|
|
this->path = path + "/" + U8Char::fromPunycode(name);
|
|
}
|
|
}
|
|
|
|
MFile::MFile(MFile* path, std::string name) : MFile(path->path + "/" + name) {
|
|
if(mstr::startsWith(name, "xn--")) {
|
|
this->path = path->path + "/" + U8Char::fromPunycode(name);
|
|
}
|
|
}
|
|
|
|
bool MFile::operator!=(nullptr_t ptr) {
|
|
return m_isNull;
|
|
}
|
|
|
|
MStream* MFile::getSourceStream(std::ios_base::openmode mode) {
|
|
// has to return OPENED stream
|
|
std::shared_ptr<MStream> containerStream(streamFile->getSourceStream(mode)); // get its base stream, i.e. zip raw file contents
|
|
Debug_printv("containerStream isRandomAccess[%d] isBrowsable[%d]", containerStream->isRandomAccess(), containerStream->isBrowsable());
|
|
|
|
MStream* decodedStream(getDecodedStream(containerStream)); // wrap this stream into decoded stream, i.e. unpacked zip files
|
|
decodedStream->url = this->url;
|
|
Debug_printv("decodedStream isRandomAccess[%d] isBrowsable[%d]", decodedStream->isRandomAccess(), decodedStream->isBrowsable());
|
|
|
|
Debug_printv("pathInStream [%s]", pathInStream.c_str());
|
|
|
|
if(decodedStream->isRandomAccess() && pathInStream != "") {
|
|
bool foundIt = decodedStream->seekPath(this->pathInStream);
|
|
|
|
if(!foundIt)
|
|
{
|
|
Debug_printv("path in stream not found");
|
|
return nullptr;
|
|
}
|
|
}
|
|
else if(decodedStream->isBrowsable() && pathInStream != "") {
|
|
auto pointedFile = decodedStream->seekNextEntry();
|
|
|
|
while (!pointedFile.empty())
|
|
{
|
|
if(mstr::compare(this->pathInStream, pointedFile))
|
|
{
|
|
//Debug_printv("returning decodedStream");
|
|
return decodedStream;
|
|
}
|
|
|
|
pointedFile = decodedStream->seekNextEntry();
|
|
}
|
|
Debug_printv("path in stream not found!");
|
|
if(pointedFile.empty())
|
|
return nullptr;
|
|
}
|
|
|
|
//Debug_printv("returning decodedStream");
|
|
return decodedStream;
|
|
};
|
|
|
|
MFile* MFile::cd(std::string newDir)
|
|
{
|
|
Debug_printv("cd requested: [%s]", newDir.c_str());
|
|
if ( streamFile != nullptr)
|
|
Debug_printv("streamFile[%s]", streamFile->url.c_str());
|
|
|
|
// OK to clarify - coming here there should be ONLY path or magicSymbol-path combo!
|
|
// NO "cd:xxxxx", no "/cd:xxxxx" ALLOWED here! ******************
|
|
//
|
|
// if you want to support LOAD"CDxxxxxx" just parse/drop the CD BEFORE calling this function
|
|
// and call it ONLY with the path you want to change into!
|
|
|
|
if(newDir.find(':') != std::string::npos)
|
|
{
|
|
// I can only guess we're CDing into another url scheme, this means we're changing whole path
|
|
return MFSOwner::File(newDir);
|
|
}
|
|
else if(newDir[0]=='_') // {CBM LEFT ARROW}
|
|
{
|
|
// user entered: CD:_ or CD_
|
|
// means: go up one directory
|
|
|
|
// user entered: CD:_DIR or CD_DIR
|
|
// means: go to a directory in the same directory as this one
|
|
return cdParent(mstr::drop(newDir,1));
|
|
}
|
|
else if(newDir[0]=='.' && newDir[1]=='.')
|
|
{
|
|
if(newDir.size()==2)
|
|
{
|
|
// user entered: CD:.. or CD..
|
|
// means: go up one directory
|
|
return cdParent();
|
|
}
|
|
else
|
|
{
|
|
// user entered: CD:..DIR or CD..DIR
|
|
// meaning: Go back one directory
|
|
return cdLocalParent(mstr::drop(newDir,2));
|
|
}
|
|
}
|
|
else if(newDir[0]=='/' && newDir[1]=='/')
|
|
{
|
|
// user entered: CD:// or CD//
|
|
// means: change to the root of stream
|
|
|
|
// user entered: CD://DIR or CD//DIR
|
|
// means: change to a dir in root of stream
|
|
return cdLocalRoot(mstr::drop(newDir,2));
|
|
}
|
|
else if(newDir[0]=='/')
|
|
{
|
|
// user entered: CD:/DIR or CD/DIR
|
|
// means: go to a directory in the same directory as this one
|
|
return cdParent(mstr::drop(newDir,1));
|
|
}
|
|
else if(newDir[0]=='^') // {CBM UP ARROW}
|
|
{
|
|
// user entered: CD:^ or CD^
|
|
// means: change to flash root
|
|
return MFSOwner::File("/");
|
|
}
|
|
else
|
|
{
|
|
//newDir = mstr::toUTF8( newDir );
|
|
|
|
// Add new directory to path
|
|
if ( !mstr::endsWith(url, "/") && newDir.size() )
|
|
url.push_back('/');
|
|
|
|
Debug_printv("> url[%s] newDir[%s]", url.c_str(), newDir.c_str());
|
|
|
|
// Add new directory to path
|
|
MFile* newPath = MFSOwner::File(url + newDir);
|
|
|
|
if(mstr::endsWith(newDir, ".url", false)) {
|
|
// we need to get actual url
|
|
|
|
//auto reader = Meat::New<MFile>(newDir);
|
|
//auto istream = reader->getSourceStream();
|
|
Meat::iostream reader(newPath);
|
|
|
|
|
|
//uint8_t url[istream->size()]; // NOPE, streams have no size!
|
|
//istream->read(url, istream->size());
|
|
std::string url;
|
|
reader >> url;
|
|
|
|
Debug_printv("url[%s]", url.c_str());
|
|
//std::string ml_url((char *)url);
|
|
|
|
delete newPath;
|
|
newPath = MFSOwner::File(url);
|
|
}
|
|
|
|
return newPath;
|
|
}
|
|
};
|
|
|
|
|
|
MFile* MFile::cdParent(std::string plus)
|
|
{
|
|
Debug_printv("url[%s] path[%s] plus[%s]", url.c_str(), path.c_str(), plus.c_str());
|
|
|
|
// drop last dir
|
|
// add plus
|
|
if(path.empty())
|
|
{
|
|
// from here we can go only to flash root!
|
|
return MFSOwner::File("/");
|
|
}
|
|
else
|
|
{
|
|
int lastSlash = url.find_last_of('/');
|
|
if ( lastSlash == url.size() - 1 )
|
|
{
|
|
lastSlash = url.find_last_of('/', url.size() - 2);
|
|
}
|
|
std::string newDir = mstr::dropLast(url, url.size() - lastSlash);
|
|
if(!plus.empty())
|
|
newDir+= ("/" + plus);
|
|
|
|
return MFSOwner::File(newDir);
|
|
}
|
|
};
|
|
|
|
MFile* MFile::cdLocalParent(std::string plus)
|
|
{
|
|
Debug_printv("url[%s] path[%s] plus[%s]", url.c_str(), path.c_str(), plus.c_str());
|
|
// drop last dir
|
|
// check if it isn't shorter than streamFile
|
|
// add plus
|
|
int lastSlash = url.find_last_of('/');
|
|
if ( lastSlash == url.size() - 1 ) {
|
|
lastSlash = url.find_last_of('/', url.size() - 2);
|
|
}
|
|
std::string parent = mstr::dropLast(url, url.size() - lastSlash);
|
|
if(parent.length()-streamFile->url.length()>1)
|
|
parent = streamFile->url;
|
|
return MFSOwner::File( parent + "/" + plus );
|
|
};
|
|
|
|
MFile* MFile::cdRoot(std::string plus)
|
|
{
|
|
Debug_printv("url[%s] path[%s] plus[%s]", url.c_str(), path.c_str(), plus.c_str());
|
|
return MFSOwner::File( "/" + plus );
|
|
};
|
|
|
|
MFile* MFile::cdLocalRoot(std::string plus)
|
|
{
|
|
Debug_printv("url[%s] path[%s] plus[%s]", url.c_str(), path.c_str(), plus.c_str());
|
|
|
|
if ( path.empty() || streamFile == nullptr ) {
|
|
// from here we can go only to flash root!
|
|
return MFSOwner::File( "/" + plus );
|
|
}
|
|
return MFSOwner::File( streamFile->url + "/" + plus );
|
|
};
|
|
|
|
// bool MFile::copyTo(MFile* dst) {
|
|
// Debug_printv("in copyTo\r\n");
|
|
// Meat::iostream istream(this);
|
|
// Meat::iostream ostream(dst);
|
|
|
|
// int rc;
|
|
|
|
// Debug_printv("in copyTo, iopen=%d oopen=%d\r\n", istream.is_open(), ostream.is_open());
|
|
|
|
// if(!istream.is_open() || !ostream.is_open())
|
|
// return false;
|
|
|
|
// Debug_printv("commencing copy\r\n");
|
|
|
|
// while((rc = istream.get())!= EOF) {
|
|
// ostream.put(rc);
|
|
// if(ostream.bad() || istream.bad())
|
|
// return false;
|
|
// }
|
|
|
|
// Debug_printv("copying finished, rc=%d\r\n", rc);
|
|
|
|
// return true;
|
|
// };
|
|
|
|
uint64_t MFile::getAvailableSpace()
|
|
{
|
|
if ( mstr::startsWith(path, (char *)"/sd") )
|
|
{
|
|
#ifdef SD_CARD
|
|
FATFS* fsinfo;
|
|
DWORD fre_clust;
|
|
|
|
if (f_getfree("/", &fre_clust, &fsinfo) == 0)
|
|
{
|
|
uint64_t total = ((uint64_t)(fsinfo->csize)) * (fsinfo->n_fatent - 2) * (fsinfo->ssize);
|
|
uint64_t used = ((uint64_t)(fsinfo->csize)) * ((fsinfo->n_fatent - 2) - (fsinfo->free_clst)) * (fsinfo->ssize);
|
|
uint64_t free = total - used;
|
|
//Debug_printv("total[%llu] used[%llu free[%llu]", total, used, free);
|
|
return free;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef FLASH_SPIFFS
|
|
size_t total = 0, used = 0;
|
|
esp_spiffs_info("flash", &total, &used);
|
|
size_t free = total - used;
|
|
//Debug_printv("total[%d] used[%d] free[%d]", total, used, free);
|
|
return free;
|
|
#elif FLASH_LITTLEFS
|
|
// TODO: Implement for LITTLEFS
|
|
Debug_printv("LITTLEFS getAvailableSpace()");
|
|
#endif
|
|
}
|
|
|
|
return 65535;
|
|
}
|
|
|
|
|
|
|