diff --git a/lib/fnjson/fnjson.cpp b/lib/fnjson/fnjson.cpp index e4cdc91a0..b86bc5513 100644 --- a/lib/fnjson/fnjson.cpp +++ b/lib/fnjson/fnjson.cpp @@ -100,9 +100,9 @@ string FNJSON::processString(string in) } } -#ifdef BUILD_IEC - mstr::toPETSCII(in); -#endif +// #ifdef BUILD_IEC +// mstr::toPETSCII(in); +// #endif #ifdef BUILD_ATARI // SIO AUX bits 0+1 control the mapping @@ -214,14 +214,14 @@ string FNJSON::getValue(cJSON *item) item = item->child; do { - #ifdef BUILD_IEC - // Convert key to PETSCII - string tempStr = string((const char *)item->string); - mstr::toPETSCII(tempStr); - ss << tempStr; - #else + // #ifdef BUILD_IEC + // // Convert key to PETSCII + // string tempStr = string((const char *)item->string); + // mstr::toPETSCII(tempStr); + // ss << tempStr; + // #else ss << item->string; - #endif + // #endif ss << lineEnding + getValue(item); } while ((item = item->next) != NULL); diff --git a/lib/meatloaf/cbm_media.cpp b/lib/meatloaf/cbm_media.cpp index 3e8076f97..2f7ef6417 100644 --- a/lib/meatloaf/cbm_media.cpp +++ b/lib/meatloaf/cbm_media.cpp @@ -49,7 +49,7 @@ void CBMImageStream::close() { }; -uint16_t CBMImageStream::seekFileSize( uint8_t start_track, uint8_t start_sector ) +uint32_t CBMImageStream::seekFileSize( uint8_t start_track, uint8_t start_sector ) { // Calculate file size seekSector(start_track, start_sector); @@ -65,36 +65,20 @@ uint16_t CBMImageStream::seekFileSize( uint8_t start_track, uint8_t start_sector seekSector( start_track, start_sector ); } while ( start_track > 0 ); blocks--; - return (blocks * (block_size - 2)) + start_sector; + return (blocks * (block_size - 2)) + start_sector - 1; }; -uint32_t CBMImageStream::position() { - return m_position; // return position within "seeked" file, not the D64 image! -}; - -size_t CBMImageStream::error() { - return m_error; // return position within "seeked" file, not the D64 image! -}; - -uint32_t CBMImageStream::available() { - // return bytes available in currently "seeked" file - return m_bytesAvailable; -}; - -uint32_t CBMImageStream::size() { - // size of the "seeked" file, not the image. - return m_length; -}; uint32_t CBMImageStream::write(const uint8_t *buf, uint32_t size) { return -1; } - uint32_t CBMImageStream::read(uint8_t* buf, uint32_t size) { uint32_t bytesRead = 0; + //Debug_printv("seekCalled[%d]", seekCalled); + if(seekCalled) { // if we have the stream set to a specific file already, either via seekNextEntry or seekPath, return bytes of the file here // or set the stream to EOF-like state, if whle file is completely read. @@ -107,6 +91,8 @@ uint32_t CBMImageStream::read(uint8_t* buf, uint32_t size) { } m_position += bytesRead; + m_bytesAvailable = m_length - m_position; + return bytesRead; }; @@ -116,4 +102,4 @@ bool CBMImageStream::isOpen() { }; -std::unordered_map ImageBroker::repo; \ No newline at end of file +std::unordered_map ImageBroker::repo; diff --git a/lib/meatloaf/cbm_media.h b/lib/meatloaf/cbm_media.h index e2b6419bd..16507bc7c 100644 --- a/lib/meatloaf/cbm_media.h +++ b/lib/meatloaf/cbm_media.h @@ -29,8 +29,6 @@ class CBMImageStream: public MStream { // MStream methods bool open() override; void close() override; - uint32_t position() override; - size_t error() override; ~CBMImageStream() { //Debug_printv("close"); @@ -67,7 +65,7 @@ class CBMImageStream: public MStream { virtual std::string readString( uint8_t size ) { uint8_t b[size] = { 0x00 }; - uint8_t r = containerStream->read( b, size ); + uint32_t r = containerStream->read( b, size ); return std::string((char *)b); } // readStringUntil = (delimiter = 0x00) => this.containerStream.readStringUntil(delimiter); @@ -80,10 +78,8 @@ class CBMImageStream: public MStream { bool seekPath(std::string path) override { return false; }; std::string seekNextEntry() override { return ""; }; - virtual uint16_t seekFileSize( uint8_t start_track, uint8_t start_sector ); + virtual uint32_t seekFileSize( uint8_t start_track, uint8_t start_sector ); - uint32_t available() override; - uint32_t size() override; uint32_t read(uint8_t* buf, uint32_t size) override; uint32_t write(const uint8_t *buf, uint32_t size); void reset() { @@ -91,6 +87,7 @@ class CBMImageStream: public MStream { m_position = 0; m_length = block_size; m_bytesAvailable = block_size; + //m_load_address = {0, 0}; } bool isOpen(); @@ -102,22 +99,17 @@ class CBMImageStream: public MStream { std::shared_ptr containerStream; bool m_isOpen = false; - uint32_t m_length = 0; - uint32_t m_bytesAvailable = 0; - uint32_t m_position = 0; - size_t m_error = 0; - CBMImageStream* decodedStream = nullptr; + CBMImageStream* decodedStream; bool show_hidden = false; - size_t block_size = 256; size_t media_header_size = 0x00; size_t entry_index = 0; // Currently selected directory entry size_t entry_count = -1; // Directory list entry count (-1 unknown) enum open_modes { OPEN_READ, OPEN_WRITE, OPEN_APPEND, OPEN_MODIFY }; - std::string file_type_label[8] = { "del", "seq", "prg", "usr", "rel", "cbm", "dir", "???" }; + std::string file_type_label[12] = { "DEL", "SEQ", "PRG", "USR", "REL", "CBM", "DIR", "SUS", "NAT", "CMD", "CFS", "???" }; virtual void seekHeader() = 0; virtual bool seekNextImageEntry() = 0; @@ -130,9 +122,9 @@ class CBMImageStream: public MStream { virtual uint8_t speedZone( uint8_t track) { return 0; }; virtual bool seekEntry( std::string filename ) { return false; }; - virtual bool seekEntry( size_t index ) { return false; }; + virtual bool seekEntry( uint16_t index ) { return false; }; - virtual size_t readFile(uint8_t* buf, size_t size) = 0; + virtual uint16_t readFile(uint8_t* buf, uint16_t size) = 0; std::string decodeType(uint8_t file_type, bool show_hidden = false); private: diff --git a/lib/meatloaf/device/flash.cpp b/lib/meatloaf/device/flash.cpp index e92601fe2..fd637d3e2 100644 --- a/lib/meatloaf/device/flash.cpp +++ b/lib/meatloaf/device/flash.cpp @@ -28,8 +28,7 @@ MFile* FlashFileSystem::getFile(std::string path) bool FlashFile::pathValid(std::string path) { - std::string s = std::string(basepath + path); - auto apath = s.c_str(); + auto apath = std::string(basepath + path).c_str(); while (*apath) { const char *slash = strchr(apath, '/'); if (!slash) { @@ -267,19 +266,19 @@ bool FlashFile::seekEntry( std::string filename ) if ( dirent->d_type != DT_DIR ) // Only want to match files not directories { if ( filename == entryFilename ) - { - closedir( d ); - return true; - } + { + closedir( d ); + return true; + } else if ( filename == "*" || mstr::compare(filename, entryFilename) ) - { - // Set filename to this filename - Debug_printv( "Found! file[%s] -> entry[%s]", filename.c_str(), entryFilename.c_str() ); - parseUrl(apath + "/" + std::string(dirent->d_name)); - closedir( d ); - return true; + { + // Set filename to this filename + Debug_printv( "Found! file[%s] -> entry[%s]", filename.c_str(), entryFilename.c_str() ); + parseUrl(apath + "/" + std::string(dirent->d_name)); + closedir( d ); + return true; + } } - } } Debug_printv( "Not Found! file[%s]", filename.c_str() ); @@ -332,7 +331,8 @@ bool FlashIStream::open() { // Set file size fseek(handle->file_h, 0, SEEK_END); //Debug_printv("IStream: past fseek 1"); - _size = ftell(handle->file_h); + m_length = ftell(handle->file_h); + m_bytesAvailable = m_length; //Debug_printv("IStream: past ftell"); fseek(handle->file_h, 0, SEEK_SET); //Debug_printv("IStream: past fseek 2"); @@ -351,11 +351,15 @@ uint32_t FlashIStream::read(uint8_t* buf, uint32_t size) { return 0; } - int bytesRead = fread((void*) buf, 1, size, handle->file_h ); - - if (bytesRead < 0) { - Debug_printv("read rc=%d\r\n", bytesRead); - return 0; + uint32_t bytesRead = 0; + if ( size > m_bytesAvailable ) + size = m_bytesAvailable; + + if ( size > 0 ) + { + bytesRead = fread((void*) buf, 1, size, handle->file_h ); + m_position += bytesRead; + m_bytesAvailable = m_length - m_position; } return bytesRead; @@ -363,12 +367,12 @@ uint32_t FlashIStream::read(uint8_t* buf, uint32_t size) { uint32_t FlashIStream::size() { - return _size; + return m_length; }; uint32_t FlashIStream::available() { if(!isOpen()) return 0; - return _size - position(); + return m_length - position(); }; @@ -434,10 +438,9 @@ void FlashHandle::obtain(std::string m_path, std::string mode) { // it will be caught by the real file open later on char *pathStr = new char[m_path.length()]; + strncpy(pathStr, m_path.data(), m_path.length()); if (pathStr) { - strncpy(pathStr, m_path.data(), m_path.length()); - // Make dirs up to the final fnamepart char *ptr = strchr(pathStr, '/'); while (ptr) { diff --git a/lib/meatloaf/device/flash.h b/lib/meatloaf/device/flash.h index f230d45e3..3ffd7b152 100644 --- a/lib/meatloaf/device/flash.h +++ b/lib/meatloaf/device/flash.h @@ -47,7 +47,7 @@ friend class FlashIStream; if (mstr::contains(name, "?") || mstr::contains(name, "*")) seekEntry( name ); - if (!pathValid(path)) + if (!pathValid(path.c_str())) m_isNull = true; else m_isNull = false; @@ -161,9 +161,6 @@ class FlashIStream: public MStream { std::string localPath; std::unique_ptr handle; - -private: - size_t _size = 0; }; diff --git a/lib/meatloaf/device/sd.h b/lib/meatloaf/device/sd.h index c357bfede..4052cbf42 100755 --- a/lib/meatloaf/device/sd.h +++ b/lib/meatloaf/device/sd.h @@ -8,7 +8,7 @@ #include "meat_io.h" -#include "../device/flash.h" +#include "flash.h" #include "fnFsSD.h" #include "peoples_url_parser.h" diff --git a/lib/meatloaf/disk/d64.cpp b/lib/meatloaf/disk/d64.cpp index 53a0b9b37..74f08074e 100644 --- a/lib/meatloaf/disk/d64.cpp +++ b/lib/meatloaf/disk/d64.cpp @@ -105,7 +105,8 @@ bool D64IStream::deallocateBlock( uint8_t track, uint8_t sector) bool D64IStream::seekEntry( std::string filename ) { uint32_t index = 1; - mstr::rtrimA0(filename); + //mstr::rtrimA0(filename); + //filename = mstr::toPETSCII2(filename); mstr::replaceAll(filename, "\\", "/"); // Read Directory Entries @@ -114,24 +115,23 @@ bool D64IStream::seekEntry( std::string filename ) while ( seekEntry( index ) ) { std::string entryFilename = entry.filename; - mstr::rtrimA0(entryFilename); - mstr::replaceAll(filename, "\\", "/"); - mstr::toASCII(entryFilename); + entryFilename = mstr::toUTF8(entryFilename); + Debug_printv("index[%d] track[%d] sector[%d] filename[%s] entry.filename[%.16s]", index, track, sector, filename.c_str(), entryFilename.c_str()); //Debug_printv("filename[%s] entry[%s]", filename.c_str(), entryFilename.c_str()); // Read Entry From Stream - if (entry.file_type & 0b00000111 && filename == "*") + if (entry.file_type & 0b00000111 && filename == "*") // Match first PRG { filename = entryFilename; return true; } - else if ( filename == entryFilename ) + else if ( filename == entryFilename ) // Match exact { return true; } - else if ( mstr::compare(filename, entryFilename) ) + else if ( mstr::compare(filename, entryFilename) ) // X?XX?X* Wildcard match { // Move stream pointer to start track/sector return true; @@ -139,6 +139,8 @@ bool D64IStream::seekEntry( std::string filename ) index++; } + + Debug_printv("File not found!"); } entry.next_track = 0; @@ -149,15 +151,15 @@ bool D64IStream::seekEntry( std::string filename ) return false; } -bool D64IStream::seekEntry( uint32_t index ) +bool D64IStream::seekEntry( uint16_t index ) { bool r = false; // Calculate Sector offset & Entry offset // 8 Entries Per Sector, 32 bytes Per Entry index--; - uint8_t sectorOffset = index / 8; - uint8_t entryOffset = (index % 8) * 32; + uint16_t sectorOffset = index / 8; + uint16_t entryOffset = (index % 8) * 32; // Debug_printv("----------"); // Debug_printv("index[%d] sectorOffset[%d] entryOffset[%d] entry_index[%d]", index, sectorOffset, entryOffset, entry_index); @@ -182,6 +184,10 @@ bool D64IStream::seekEntry( uint32_t index ) r = seekSector( entry.next_track, entry.next_sector ); } + // Seek failed + if ( !r ) + return false; + containerStream->read((uint8_t *)&entry, sizeof(entry)); next_track = entry.next_track; next_sector = entry.next_sector; @@ -262,8 +268,7 @@ uint16_t D64IStream::blocksFree() return free_count; } -size_t D64IStream::readFile(uint8_t* buf, size_t size) { - size_t bytesRead = 0; +uint16_t D64IStream::readFile(uint8_t* buf, uint16_t size) { if ( sector_offset % block_size == 0 ) { @@ -275,16 +280,22 @@ size_t D64IStream::readFile(uint8_t* buf, size_t size) { //Debug_printv("next_track[%d] next_sector[%d] sector_offset[%d]", next_track, next_sector, sector_offset); } - bytesRead += containerStream->read(buf, size); - sector_offset += bytesRead; - m_bytesAvailable -= bytesRead; - - if ( sector_offset % block_size == 0 ) + uint16_t bytesRead = 0; + if ( size > m_bytesAvailable ) + size = m_bytesAvailable; + + if ( size > 0 ) { - // We are at the end of the block - // Follow track/sector link to move to next block - seekSector( next_track, next_sector ); - //Debug_printv("track[%d] sector[%d] sector_offset[%d]", track, sector, sector_offset); + bytesRead += containerStream->read(buf, size); + sector_offset += bytesRead; + + if ( sector_offset % block_size == 0 ) + { + // We are at the end of the block + // Follow track/sector link to move to next block + seekSector( next_track, next_sector ); + //Debug_printv("track[%d] sector[%d] sector_offset[%d]", track, sector, sector_offset); + } } // if ( !bytesRead ) @@ -382,7 +393,7 @@ bool D64File::rewindDirectory() { media_blocks_free = image->blocksFree(); media_block_size = image->block_size; media_image = name; - mstr::toASCII(media_image); + //mstr::toUTF8(media_image); return true; } @@ -404,7 +415,7 @@ MFile* D64File::getNextFileInDir() { if ( r ) { std::string fileName = image->entry.filename; - mstr::rtrimA0(fileName); + //mstr::rtrimA0(fileName); mstr::replaceAll(fileName, "/", "\\"); //Debug_printv( "entry[%s]", (streamFile->url + "/" + fileName).c_str() ); auto file = MFSOwner::File(streamFile->url + "/" + fileName); @@ -424,15 +435,15 @@ time_t D64File::getLastWrite() { } time_t D64File::getCreationTime() { - tm entry_time; + tm *entry_time = 0; auto entry = ImageBroker::obtain(streamFile->url)->entry; - entry_time.tm_year = entry.year + 1900; - entry_time.tm_mon = entry.month; - entry_time.tm_mday = entry.day; - entry_time.tm_hour = entry.hour; - entry_time.tm_min = entry.minute; + entry_time->tm_year = entry.year + 1900; + entry_time->tm_mon = entry.month; + entry_time->tm_mday = entry.day; + entry_time->tm_hour = entry.hour; + entry_time->tm_min = entry.minute; - return mktime(&entry_time); + return mktime(entry_time); } bool D64File::exists() { diff --git a/lib/meatloaf/disk/d64.h b/lib/meatloaf/disk/d64.h index 68f310393..fbcf369ff 100644 --- a/lib/meatloaf/disk/d64.h +++ b/lib/meatloaf/disk/d64.h @@ -1,4 +1,5 @@ -// .D64 - The D64 disk image format +// .D64, .D41 - 1541 disk image format +// // https://vice-emu.sourceforge.io/vice_17.html#SEC345 // https://ist.uwaterloo.ca/~schepers/formats/D64.TXT // https://ist.uwaterloo.ca/~schepers/formats/GEOS.TXT @@ -6,6 +7,7 @@ // - disucssion of disk id in sector missing from d64 file format is interesting // https://www.c64-wiki.com/wiki/Disk_Image // http://unusedino.de/ec64/technical3.html +// http://www.baltissen.org/newhtm/diskimag.htm // #ifndef MEATLOAF_MEDIA_D64 @@ -153,38 +155,38 @@ class D64IStream : public CBMImageStream { // // DOLPHIN DOS // partitions[0].block_allocation_map.push_back( - // { - // 18, // track - // 0, // sector - // 0xAC, // offset - // 36, // start_track - // 40, // end_track - // 4 // byte_count - // } + // { + // 18, // track + // 0, // sector + // 0xAC, // offset + // 36, // start_track + // 40, // end_track + // 4 // byte_count + // } // ); // // SPEED DOS // partitions[0].block_allocation_map.push_back( - // { - // 18, // track - // 0, // sector - // 0xC0, // offset - // 36, // start_track - // 40, // end_track - // 4 // byte_count - // } + // { + // 18, // track + // 0, // sector + // 0xC0, // offset + // 36, // start_track + // 40, // end_track + // 4 // byte_count + // } // ); // // PrologicDOS // partitions[0].block_allocation_map.push_back( - // { - // 18, // track - // 0, // sector + // { + // 18, // track + // 0, // sector // 0x90, // offset - // 36, // start_track - // 40, // end_track - // 4 // byte_count - // } + // 36, // start_track + // 40, // end_track + // 4 // byte_count + // } // ); // partitions[0].header_offset = 0xA4; @@ -218,7 +220,7 @@ class D64IStream : public CBMImageStream { } virtual bool seekPath(std::string path) override; - size_t readFile(uint8_t* buf, size_t size) override; + uint16_t readFile(uint8_t* buf, uint16_t size) override; Header header; // Directory header data Entry entry; // Directory entry data @@ -238,7 +240,7 @@ class D64IStream : public CBMImageStream { void sendListing(); bool seekEntry( std::string filename ); - bool seekEntry( uint32_t index = 0 ); + bool seekEntry( uint16_t index = 0 ); std::string readBlock( uint8_t track, uint8_t sector ); @@ -268,7 +270,7 @@ class D64File: public MFile { isDir = is_dir; media_image = name; - mstr::toASCII(media_image); + isPETSCII = true; }; ~D64File() { @@ -277,12 +279,6 @@ class D64File: public MFile { MStream* createIStream(std::shared_ptr containerIstream) override; - std::string petsciiName() override { - // It's already in PETSCII - mstr::replaceAll(name, "\\", "/"); - return name; - } - bool isDirectory() override; bool rewindDirectory() override; MFile* getNextFileInDir() override; @@ -315,7 +311,13 @@ class D64FileSystem: public MFileSystem bool handles(std::string fileName) { //Serial.printf("handles w dnp %s %d\r\n", fileName.rfind(".dnp"), fileName.length()-4); - return byExtension(".d64", fileName); + return byExtension( + { + ".d64", + ".d41" + }, + fileName + ); } D64FileSystem(): MFileSystem("d64") {}; diff --git a/lib/meatloaf/disk/d71.h b/lib/meatloaf/disk/d71.h index 964ed938a..ae4f34729 100644 --- a/lib/meatloaf/disk/d71.h +++ b/lib/meatloaf/disk/d71.h @@ -1,4 +1,5 @@ -// .D71 - The D71 disk image format +// .D71 - 1571 disk image format +// // https://vice-emu.sourceforge.io/vice_17.html#SEC373 // https://ist.uwaterloo.ca/~schepers/formats/D71.TXT // diff --git a/lib/meatloaf/disk/d80.h b/lib/meatloaf/disk/d80.h index 41012b942..b8dc7d49e 100644 --- a/lib/meatloaf/disk/d80.h +++ b/lib/meatloaf/disk/d80.h @@ -1,4 +1,5 @@ // .D80 - This is a sector-for-sector copy of an 8050 floppy disk +// // https://vice-emu.sourceforge.io/vice_17.html#SEC360 // https://ist.uwaterloo.ca/~schepers/formats/D80-D82.TXT // diff --git a/lib/meatloaf/disk/d81.h b/lib/meatloaf/disk/d81.h index d95a46900..92aef6bef 100644 --- a/lib/meatloaf/disk/d81.h +++ b/lib/meatloaf/disk/d81.h @@ -1,4 +1,5 @@ // .D81 - This is a byte for byte copy of a physical 1581 disk +// // https://vice-emu.sourceforge.io/vice_17.html#SEC354 // https://ist.uwaterloo.ca/~schepers/formats/D81.TXT // diff --git a/lib/meatloaf/disk/d82.h b/lib/meatloaf/disk/d82.h index 44a386c27..1973982a9 100644 --- a/lib/meatloaf/disk/d82.h +++ b/lib/meatloaf/disk/d82.h @@ -1,4 +1,5 @@ // .D82 - This is a sector-for-sector copy of an 8250 floppy disk +// // https://vice-emu.sourceforge.io/vice_17.html#SEC363 // https://ist.uwaterloo.ca/~schepers/formats/D80-D82.TXT // diff --git a/lib/meatloaf/disk/d8b.h b/lib/meatloaf/disk/d8b.h index 11fc7840f..9da90f0ce 100644 --- a/lib/meatloaf/disk/d8b.h +++ b/lib/meatloaf/disk/d8b.h @@ -1,4 +1,5 @@ // .D8B - Backbit D8B disk format +// // https://www.backbit.io/downloads/Docs/BackBit%20Cartridge%20Documentation.pdf#page=20 // https://github.com/evietron/BackBit-Tool // diff --git a/lib/meatloaf/disk/d90.h b/lib/meatloaf/disk/d90.h index 63103cab0..300e4eb3b 100644 --- a/lib/meatloaf/disk/d90.h +++ b/lib/meatloaf/disk/d90.h @@ -1,4 +1,5 @@ // .D90 - The D90 image is bit-for-bit copy of the hard drives in the D9090 and D9060 +// // https://vice-emu.sourceforge.io/vice_17.html#SEC388 // http://www.baltissen.org/newhtm/diskimag.htm // @@ -140,10 +141,10 @@ class D90FileSystem: public MFileSystem } bool handles(std::string fileName) { - return byExtension(".D90", fileName); + return byExtension(".d90", fileName); } - D90FileSystem(): MFileSystem("D90") {}; + D90FileSystem(): MFileSystem("d90") {}; }; diff --git a/lib/meatloaf/disk/dfi.h b/lib/meatloaf/disk/dfi.h index b1251b39a..f1b5c17bb 100644 --- a/lib/meatloaf/disk/dfi.h +++ b/lib/meatloaf/disk/dfi.h @@ -1,4 +1,5 @@ // .DFI - DreamLoad File Archive +// // https://www.lemon64.com/forum/viewtopic.php?t=37415#458552 // diff --git a/lib/meatloaf/disk/dnp.h b/lib/meatloaf/disk/dnp.h index b272c6baa..eac4159d0 100644 --- a/lib/meatloaf/disk/dnp.h +++ b/lib/meatloaf/disk/dnp.h @@ -1,4 +1,5 @@ // .DNP - CMD hard Disk Native Partition +// // https://ist.uwaterloo.ca/~schepers/formats/D2M-DNP.TXT // diff --git a/lib/meatloaf/file/p00.cpp b/lib/meatloaf/file/p00.cpp index 3b263aa2e..9cffea683 100644 --- a/lib/meatloaf/file/p00.cpp +++ b/lib/meatloaf/file/p00.cpp @@ -4,8 +4,8 @@ * Streams ********************************************************/ -size_t P00IStream::readFile(uint8_t* buf, size_t size) { - size_t bytesRead = 0; +uint16_t P00IStream::readFile(uint8_t* buf, uint16_t size) { + uint16_t bytesRead = 0; bytesRead += containerStream->read(buf, size); m_bytesAvailable -= bytesRead; diff --git a/lib/meatloaf/file/p00.h b/lib/meatloaf/file/p00.h index 14cac9e47..47479fe76 100644 --- a/lib/meatloaf/file/p00.h +++ b/lib/meatloaf/file/p00.h @@ -57,7 +57,7 @@ class P00IStream : public CBMImageStream { return ""; }; - size_t readFile(uint8_t* buf, size_t size) override; + uint16_t readFile(uint8_t* buf, uint16_t size) override; Header header; @@ -83,12 +83,6 @@ class P00File: public MFile { MStream* createIStream(std::shared_ptr containerIstream) override; - std::string petsciiName() override { - // It's already in PETSCII - mstr::replaceAll(name, "\\", "/"); - return name; - } - bool isDirectory() override { return false; };; bool rewindDirectory() override { return false; };; MFile* getNextFileInDir() override { return nullptr; };; diff --git a/lib/meatloaf/meat_buffer.h b/lib/meatloaf/meat_buffer.h index 61b8c8104..ca996150c 100644 --- a/lib/meatloaf/meat_buffer.h +++ b/lib/meatloaf/meat_buffer.h @@ -6,15 +6,22 @@ #include "meat_io.h" -using namespace std; - namespace Meat { /******************************************************** * C++ Input MFile buffer ********************************************************/ - template > + struct my_char_traits : public std::char_traits + { + typedef std::char_traits::int_type int_type; + static constexpr int_type nda() noexcept + { + return static_cast(_MEAT_NO_DATA_AVAIL); + } + }; + + template class mfilebuf : public std::basic_filebuf { std::unique_ptr mstream; @@ -37,8 +44,8 @@ namespace Meat mfilebuf() { - ibuffer = new char[ibufsize]; - obuffer = new char[obufsize]; + ibuffer = new char[ibufsize+1]; + obuffer = new char[obufsize+1]; }; ~mfilebuf() @@ -52,10 +59,16 @@ namespace Meat close(); } - std::filebuf *doOpen() + std::filebuf *doOpen(std::ios_base::openmode mode) { // Debug_println("In filebuf open pre reset mistream"); - mstream.reset(mfile->meatStream()); + if (mode == std::ios_base::in) + mstream.reset(mfile->meatStream()); + else + { + // TODO - ok we have to obtain the out stream here SOMEHOW + } + // Debug_println("In filebuf open post reset mistream"); if (mstream->isOpen()) { @@ -67,25 +80,25 @@ namespace Meat return nullptr; } - std::filebuf *open(const char* filename) + std::filebuf *open(const char *filename, std::ios_base::openmode mode) { // Debug_println("In filebuf open"); mfile.reset(MFSOwner::File(filename)); - return doOpen(); + return doOpen(mode); }; - std::filebuf *open(const std::string filename) + std::filebuf *open(const std::string filename, std::ios_base::openmode mode) { // Debug_println("In filebuf open"); mfile.reset(MFSOwner::File(filename)); - return doOpen(); + return doOpen(mode); }; virtual bool close() { if (is_open()) { - // Debug_printv("closing in filebuf\r\n"); + // Debug_printv("closing in filebuf\n"); sync(); mstream->close(); return true; @@ -95,6 +108,8 @@ namespace Meat bool is_open() const { + Debug_printv("is_open"); + if (mstream == nullptr) return false; else @@ -123,31 +138,31 @@ namespace Meat // https://newbedev.com/how-to-write-custom-input-stream-in-c - - static int nda() - { return static_cast(_MEAT_NO_DATA_AVAIL); } - // let's get a byte relative to current egptr int operator[](int index) { // 1. let's check if our index is within current buffer POINTERS // gptr = current character (get pointer) // egptr = one past end of get area - if(this->gptr() == this->egptr()) + if (this->gptr() == this->egptr()) underflow(); - if(this->gptr() < this->egptr()) { + if (this->gptr() < this->egptr()) + { Debug_printf("%d char is within gptr-egptr range", index); // or - send the char across IEC to our C64 return std::char_traits::to_int_type(this->gptr()[index]); } - else { + else + { Debug_printf("Index out of current buffer %d", this->gptr() - this->egptr()); - return _MEAT_NO_DATA_AVAIL; + + return my_char_traits::nda(); } } int underflow() override { + if (!is_open()) { return std::char_traits::eof(); @@ -158,32 +173,33 @@ namespace Meat // no more characters are available, size == 0. // auto buffer = reader->read(); - //Debug_printv("--mfilebuf underflow, calling read!"); - int readCount = mstream->read((uint8_t *)ibuffer, ibufsize); - if(readCount == _MEAT_NO_DATA_AVAIL) { - //Debug_printv("--mfilebuf underflow no data available, will teturn=%d!", nda()); + Debug_printv("meat buffer underflow, readCount=%d", readCount); + + // beg, curr, end <=> eback, gptr, egptr + if (readCount == _MEAT_NO_DATA_AVAIL) + { // if gptr >= egptr - sgetc will call underflow again: // gptr egptr - this->setg(ibuffer, ibuffer, ibuffer); // beg, curr, end <=> eback, gptr, egptr - ibuffer[0]=_MEAT_NO_DATA_AVAIL; // this will be picked up by commodore server test! - return _MEAT_NO_DATA_AVAIL; - //return nda(); - //return std::char_traits::to_int_type('x'); // this is not read - //return std::char_traits::eof(); + Debug_printv("meat underflow received _MEAT_NO_DATA_AVAIL!"); + //this->setg(ibuffer, ibuffer, ibuffer); + + return my_char_traits::nda(); } - else if(readCount < 0) { - Debug_printv("--mfilebuf different read error, RC=%d!", readCount); - this->setg(ibuffer, ibuffer, ibuffer); + else if (readCount <= 0) + { + Debug_printv("--mfilebuf read error or EOF, RC=%d!", readCount); + //this->setg(ibuffer, ibuffer, ibuffer); return std::char_traits::eof(); } - else { + else + { currBuffEnd = mstream->position(); - currBuffStart = currBuffEnd-readCount; // this is where our buffer data starts - - //Debug_printv("--mfilebuf underflow, read bytes=%d--", readCount); + currBuffStart = currBuffEnd - readCount; // this is where our buffer data starts + // Debug_printv("--mfilebuf underflow, read bytes=%d--", readCount); + // beg, curr, end <=> eback, gptr, egptr this->setg(ibuffer, ibuffer, ibuffer + readCount); } } @@ -239,9 +255,10 @@ namespace Meat if (ch != EOF) { *end++ = ch; + this->pbump(1); } - Debug_printv("%d bytes in buffer will be written", end-this->pbase()); + Debug_printv("%d bytes in buffer will be written", end - this->pbase()); uint8_t *pBase = (uint8_t *)this->pbase(); @@ -258,7 +275,7 @@ namespace Meat return ch; }; - std::streampos seekposforce(std::streampos __pos, std::ios_base::openmode __mode = std::ios_base::in | std::ios_base::out) + std::streampos seekposforce(std::streampos __pos, std::ios_base::openmode __mode = std::ios_base::in) { std::streampos __ret = std::streampos(off_type(-1)); @@ -272,7 +289,7 @@ namespace Meat return __ret; } - std::streampos seekpos(std::streampos __pos, std::ios_base::openmode __mode = std::ios_base::in | std::ios_base::out) override + std::streampos seekpos(std::streampos __pos, std::ios_base::openmode __mode = std::ios_base::in) override { std::streampos __ret = std::streampos(off_type(-1)); @@ -370,9 +387,6 @@ namespace Meat }; }; - - - // [27.8.1.11] Template class basic_fstream /** * @brief Controlling input and output for files. @@ -426,35 +440,34 @@ namespace Meat * @param __mode Open file in specified mode (see std::ios_base). */ explicit basic_fstream(const char *__s, - std::ios_base::openmode __mode = std::ios_base::in | std::ios_base::out) + std::ios_base::openmode __mode = std::ios_base::in) : __iostream_type(0), _M_filebuf() { this->init(&_M_filebuf); this->open(__s, __mode); } - explicit basic_fstream(MFile* fi, - std::ios_base::openmode __mode = std::ios_base::in | std::ios_base::out) + explicit basic_fstream(MFile *fi, + std::ios_base::openmode __mode = std::ios_base::in) : __iostream_type(0), _M_filebuf() { this->init(&_M_filebuf); this->open(fi->url.c_str(), __mode); } - #if __cplusplus >= 201103L /** * @brief Create an input/output file stream. * @param __s Null terminated string specifying the filename. * @param __mode Open file in specified mode (see std::ios_base). */ - explicit basic_fstream(const std::string &__s, std::ios_base::openmode __mode = std::ios_base::in | std::ios_base::out) : __iostream_type(0), _M_filebuf() + explicit basic_fstream(const std::string &__s, std::ios_base::openmode __mode = std::ios_base::in) : __iostream_type(0), _M_filebuf() { this->init(&_M_filebuf); this->open(__s, __mode); } -#if __cplusplus >= 201703L +#if __cplusplus >= 201703L && __cplusplus < 202002L /** * @param Create an input/output file stream. * @param __s filesystem::path specifying the filename. @@ -464,7 +477,8 @@ namespace Meat basic_fstream(const _Path &__s, ios_base::openmode __mode = ios_base::in | ios_base::out) : basic_fstream(__s.c_str(), __mode) - { } + { + } #endif // C++17 basic_fstream(const basic_fstream &) = delete; @@ -524,6 +538,7 @@ namespace Meat */ bool is_open() { + Debug_printv("is_open"); return _M_filebuf.is_open(); } @@ -531,6 +546,7 @@ namespace Meat // 365. Lack of const-qualification in clause 27 bool is_open() const { + Debug_printv("is_open const"); return _M_filebuf.is_open(); } @@ -542,9 +558,9 @@ namespace Meat * Calls @c std::basic_filebuf::open(__s,__mode). If that * function fails, @c failbit is set in the stream's error state. */ - void open(const char *__s, std::ios_base::openmode __mode = std::ios_base::in | std::ios_base::out) + void open(const char *__s, std::ios_base::openmode __mode = std::ios_base::in) { - if (!_M_filebuf.open(__s/*, __mode*/)) + if (!_M_filebuf.open(__s, __mode)) this->setstate(std::ios_base::failbit); else // _GLIBCXX_RESOLVE_LIB_DEFECTS @@ -561,9 +577,9 @@ namespace Meat * Calls @c std::basic_filebuf::open(__s,__mode). If that * function fails, @c failbit is set in the stream's error state. */ - void open(const std::string &__s, std::ios_base::openmode __mode = std::ios_base::in | std::ios_base::out) + void open(const std::string &__s, std::ios_base::openmode __mode = std::ios_base::in) { - if (!_M_filebuf.open(__s/*, __mode*/)) + if (!_M_filebuf.open(__s, __mode)) this->setstate(std::ios_base::failbit); else // _GLIBCXX_RESOLVE_LIB_DEFECTS @@ -571,7 +587,7 @@ namespace Meat this->clear(); } -#if __cplusplus >= 201703L +#if __cplusplus >= 201703L && __cplusplus < 202002L /** * @brief Opens an external file. * @param __s The name of the file, as a filesystem::path. @@ -618,6 +634,39 @@ namespace Meat U8Char wide = U8Char(c); (*this) << wide.toUtf8(); } + + bool nda() const { + return (this->rdstate() & ndabit) != 0; + } + + basic_fstream<_CharT, _Traits> &checkNda() + { + // first naive implementation... + + /* + istream->good(); // rdstate == 0 + istream->bad(); // rdstate() & badbit) != 0 + istream->eof(); // rdstate() & eofbit) != 0 + istream->fail(); // >rdstate() & (badbit | failbit)) != 0 + istream->rdstate(); + istream->setstate(); + _S_goodbit = 0, + _S_badbit = 1L << 0, + _S_eofbit = 1L << 1, + _S_failbit = 1L << 2, + _S_ios_iostate_end = 1L << 16, + _S_ios_iostate_max = __INT_MAX__, + _S_ios_iostate_min = ~__INT_MAX__ + */ + + const int_type __cb = this->rdbuf()->sgetc(); // check next char, don't advance + if(traits_type::eq_int_type(__cb, my_char_traits::nda())) { + // it's NDA - ok, so let's set a proper mark on this stream + this->rdbuf()->sbumpc(); // skip nda and... + this->setstate((std::ios_base::iostate)ndabit); // set state to nda + } + return *this; + } }; typedef basic_fstream iostream; @@ -628,8 +677,8 @@ namespace Meat // return is; // } - //https://stdcxx.apache.org/doc/stdlibug/39-3.html - //https://cplusplus.com/reference/istream/iostream/ + // https://stdcxx.apache.org/doc/stdlibug/39-3.html + // https://cplusplus.com/reference/istream/iostream/ } #endif /* MEATLOAF_BUFFER */ diff --git a/lib/meatloaf/meat_io.cpp b/lib/meatloaf/meat_io.cpp index ac5b27924..2c200fc23 100644 --- a/lib/meatloaf/meat_io.cpp +++ b/lib/meatloaf/meat_io.cpp @@ -39,14 +39,17 @@ // Network #include "network/http.h" -#include "network/ml.h" #include "network/tnfs.h" // #include "network/ipfs.h" // #include "network/tnfs.h" // #include "network/smb.h" -// #include "network/cs.h" // #include "network/ws.h" +// Service +// #include "service/cs.h" +#include "service/ml.h" + + // Tape #include "tape/t64.h" #include "tape/tcrt.h" @@ -59,7 +62,7 @@ FlashFileSystem defaultFS; #ifdef SD_CARD SDFileSystem sdFS; -#endif +#endif // Scheme HttpFileSystem httpFS; @@ -363,71 +366,12 @@ MStream* MFile::meatStream() { }; -MFile* MFile::parent(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::localParent(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::root(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::localRoot(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 ); -}; 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()); + 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! ****************** @@ -447,7 +391,7 @@ MFile* MFile::cd(std::string newDir) // user entered: CD:_DIR or CD_DIR // means: go to a directory in the same directory as this one - return parent(mstr::drop(newDir,1)); + return cdParent(mstr::drop(newDir,1)); } else if(newDir[0]=='.' && newDir[1]=='.') { @@ -455,13 +399,13 @@ MFile* MFile::cd(std::string newDir) { // user entered: CD:.. or CD.. // means: go up one directory - return parent(); + return cdParent(); } else { // user entered: CD:..DIR or CD..DIR // meaning: Go back one directory - return localParent(mstr::drop(newDir,2)); + return cdLocalParent(mstr::drop(newDir,2)); } } else if(newDir[0]=='/' && newDir[1]=='/') @@ -471,13 +415,13 @@ MFile* MFile::cd(std::string newDir) // user entered: CD://DIR or CD//DIR // means: change to a dir in root of stream - return localRoot(mstr::drop(newDir,2)); + 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 parent(mstr::drop(newDir,1)); + return cdParent(mstr::drop(newDir,1)); } else if(newDir[0]=='^') // {CBM UP ARROW} { @@ -485,23 +429,10 @@ MFile* MFile::cd(std::string newDir) // means: change to flash root return MFSOwner::File("/"); } - - - // if(newDir[0]=='@' /*&& newDir[1]=='/' let's be consistent!*/) { - // if(newDir.size() == 1) { - // // user entered: CD:@ or CD@ - // // meaning: go to the .sys folder - // return MFSOwner::File("/.sys"); - // } - // else { - // // user entered: CD:@FOLDER or CD@FOLDER - // // meaning: go to a folder in .sys folder - // return MFSOwner::File("/.sys/" + mstr::drop(newDir,1)); - // } - // } - else { + newDir = mstr::toUTF8( newDir ); + // Add new directory to path if ( !mstr::endsWith(url, "/") && newDir.size() ) url.push_back('/'); @@ -513,6 +444,66 @@ MFile* MFile::cd(std::string newDir) } }; + +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); diff --git a/lib/meatloaf/meat_io.h b/lib/meatloaf/meat_io.h index 3be3df217..35541f3b3 100644 --- a/lib/meatloaf/meat_io.h +++ b/lib/meatloaf/meat_io.h @@ -15,7 +15,10 @@ #include "string_utils.h" #include "U8Char.h" -#define _MEAT_NO_DATA_AVAIL -69 +#define _MEAT_NO_DATA_AVAIL (std::ios_base::eofbit) + +static const std::ios_base::iostate ndabit = _MEAT_NO_DATA_AVAIL; + /******************************************************** * Universal file @@ -39,11 +42,7 @@ class MFile : public PeoplesUrlParser { } }; - MFile* parent(std::string = ""); - MFile* localParent(std::string); - MFile* root(std::string); - MFile* localRoot(std::string); - + bool isPETSCII = false; std::string media_header; std::string media_id; std::string media_archive; @@ -55,16 +54,14 @@ class MFile : public PeoplesUrlParser { // bool copyTo(MFile* dst); - virtual std::string petsciiName() { - std::string pname = name; - mstr::toPETSCII(pname); - return pname; - } - // has to return OPENED stream virtual MStream* meatStream(); - virtual MFile* cd(std::string newDir); + MFile* cd(std::string newDir); + MFile* cdParent(std::string = ""); + MFile* cdLocalParent(std::string); + MFile* cdRoot(std::string); + MFile* cdLocalRoot(std::string); virtual bool isDirectory() = 0; virtual bool rewindDirectory() = 0 ; virtual MFile* getNextFileInDir() = 0 ; @@ -77,6 +74,13 @@ class MFile : public PeoplesUrlParser { virtual time_t getCreationTime() = 0 ; virtual uint32_t size() = 0; virtual uint64_t getAvailableSpace(); + virtual uint32_t blocks() { + auto s = size(); + if ( s > 0 && s < media_block_size ) + return 1; + else + return ( s / media_block_size ); + } virtual bool isText() { return mstr::isText(extension); @@ -113,6 +117,16 @@ class MFileSystem { return mstr::endsWith(fileName, ext, false); } + static bool byExtension(std::vector ext, std::string fileName) { + for ( auto & e : ext ) + { + if ( mstr::endsWith(fileName, e.c_str(), false) ) + return true; + } + + return false; + } + protected: const char* symbol = nullptr; bool m_isMounted = false; diff --git a/lib/meatloaf/meat_stream.h b/lib/meatloaf/meat_stream.h index b35ebe5e9..84fc4c77a 100644 --- a/lib/meatloaf/meat_stream.h +++ b/lib/meatloaf/meat_stream.h @@ -31,14 +31,54 @@ // TCP_CLENT_SOCKET = clear bit 5 // TCP_SERVER_SOCKET = set bit 5 -class MStream { +class MStream +{ +protected: + uint32_t m_length = 0; + uint32_t m_bytesAvailable = 0; + uint32_t m_position = 0; + uint8_t m_load_address[2] = {0, 0}; + size_t m_error = 0; + public: virtual ~MStream() {}; - virtual uint32_t available() = 0; - virtual uint32_t size() = 0; - virtual uint32_t position() = 0; - virtual size_t error() = 0; + uint8_t secondaryAddress = 0; + std::string url = ""; + + bool has_subdirs = true; + size_t block_size = 256; + + virtual uint32_t size() { + return m_length; + }; + + virtual uint32_t available() { + return m_bytesAvailable; + }; + + virtual uint32_t position() { + return m_position; + } + + virtual size_t error() { + return m_error; + } + + virtual uint32_t blocks() { + if ( m_length > 0 && m_length < block_size ) + return 1; + else + return ( m_length / block_size ); + } + + virtual bool eos() { +// Debug_printv("m_length[%d] m_bytesAvailable[%d] m_position[%d]", m_length, m_bytesAvailable, m_position); + if ( m_bytesAvailable == 0 ) + return true; + + return false; + } virtual void reset() {}; virtual bool isOpen() = 0; @@ -51,11 +91,6 @@ class MStream { virtual uint32_t write(const uint8_t *buf, uint32_t size) = 0; virtual uint32_t read(uint8_t* buf, uint32_t size) = 0; - uint8_t secondaryAddress = 0; - std::string url = ""; - - bool has_subdirs = true; - virtual bool seek(uint32_t pos, int mode) { if(mode == SEEK_SET) { return seek(pos); diff --git a/lib/meatloaf/network/http.cpp b/lib/meatloaf/network/http.cpp index 032d3d709..1fb8bcd35 100644 --- a/lib/meatloaf/network/http.cpp +++ b/lib/meatloaf/network/http.cpp @@ -131,6 +131,11 @@ bool HttpIStream::open() { else if(secondaryAddress == 2) r = m_http.POST(url); + if ( r ) { + m_length = m_http.m_length; + m_bytesAvailable = m_length; + } + return r; } @@ -150,7 +155,18 @@ bool HttpIStream::seek(uint32_t pos) { } uint32_t HttpIStream::read(uint8_t* buf, uint32_t size) { - return m_http.read(buf, size); + uint32_t bytesRead = 0; + if ( size > m_bytesAvailable ) + size = m_bytesAvailable; + + if ( size > 0 ) + { + bytesRead = m_http.read(buf, size); + m_position += bytesRead; + m_bytesAvailable = m_length - m_position; + } + + return bytesRead; }; uint32_t HttpIStream::write(const uint8_t *buf, uint32_t size) { @@ -279,7 +295,7 @@ bool MeatHttpClient::seek(uint32_t pos) { Debug_printv("Seek successful"); m_position = pos; - m_bytesAvailable = m_length-pos; + m_bytesAvailable = m_length - m_position; return true; } } @@ -313,8 +329,8 @@ bool MeatHttpClient::seek(uint32_t pos) { } } - m_bytesAvailable = m_length-pos; m_position = pos; + m_bytesAvailable = m_length - m_position; Debug_printv("stream opened[%s]", url.c_str()); return true; @@ -328,8 +344,8 @@ uint32_t MeatHttpClient::read(uint8_t* buf, uint32_t size) { auto bytesRead= esp_http_client_read(m_http, (char *)buf, size ); if(bytesRead>0) { - m_bytesAvailable -= bytesRead; m_position+=bytesRead; + m_bytesAvailable = m_length - m_position; } return bytesRead; } @@ -467,16 +483,25 @@ esp_err_t MeatHttpClient::_http_event_handler(esp_http_client_event_t *evt) else { //Debug_printv("no match"); - meatClient->url += evt->header_value; + if ( mstr::startsWith(evt->header_value, (char *)"/") ) + { + // Absolute path redirect + PeoplesUrlParser u; + u.parseUrl( meatClient->url ); + meatClient->url = u.root() + evt->header_value; + } + else + { + // Relative path redirect + meatClient->url += evt->header_value; + } } //Debug_printv("new url '%s'", meatClient->url.c_str()); } // Allow override in lambda - if(meatClient != nullptr) { - meatClient->onHeader(evt->header_key, evt->header_value); - } + meatClient->onHeader(evt->header_key, evt->header_value); break; diff --git a/lib/meatloaf/network/ml.cpp b/lib/meatloaf/network/ml.cpp deleted file mode 100644 index a94517255..000000000 --- a/lib/meatloaf/network/ml.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "ml.h" diff --git a/lib/meatloaf/network/ml.h b/lib/meatloaf/network/ml.h deleted file mode 100644 index d06e2ff80..000000000 --- a/lib/meatloaf/network/ml.h +++ /dev/null @@ -1,46 +0,0 @@ -// ML:// - Meatloaf Server Protocol -// - - -#ifndef MEATLOAF_SCHEME_ML -#define MEATLOAF_SCHEME_ML - -#include "network/http.h" - -#include "peoples_url_parser.h" - - -/******************************************************** - * FS - ********************************************************/ - -class MLFileSystem: public MFileSystem -{ - MFile* getFile(std::string path) override { - if ( path.size() == 0 ) - return nullptr; - - //Debug_printv("MLFileSystem::getFile(%s)", path.c_str()); - PeoplesUrlParser urlParser; - urlParser.parseUrl(path); - - //Debug_printv("url[%s]", urlParser.name.c_str()); - std::string ml_url = "https://api.meatloaf.cc/?" + urlParser.name; - //Debug_printv("ml_url[%s]", ml_url.c_str()); - - //Debug_printv("url[%s]", ml_url.c_str()); - - return new HttpFile(ml_url); - } - - bool handles(std::string name) { - std::string pattern = "ml:"; - return mstr::startsWith(name, pattern.c_str(), false); - } - -public: - MLFileSystem(): MFileSystem("meatloaf") {}; -}; - - -#endif // MEATLOAF_SCHEME_ML \ No newline at end of file diff --git a/lib/meatloaf/network/tnfs.cpp b/lib/meatloaf/network/tnfs.cpp index edd9d85f5..1aa02bf90 100644 --- a/lib/meatloaf/network/tnfs.cpp +++ b/lib/meatloaf/network/tnfs.cpp @@ -13,8 +13,7 @@ bool TNFSFile::pathValid(std::string path) { - std::string s = std::string(basepath + path); - auto apath = s.c_str(); + auto apath = std::string(basepath + path).c_str(); while (*apath) { const char *slash = strchr(apath, '/'); if (!slash) { @@ -314,7 +313,8 @@ bool TNFSIStream::open() { // Set file size fseek(handle->file_h, 0, SEEK_END); //Debug_printv("IStream: past fseek 1"); - _size = ftell(handle->file_h); + m_length = ftell(handle->file_h); + m_bytesAvailable = m_length; //Debug_printv("IStream: past ftell"); fseek(handle->file_h, 0, SEEK_SET); //Debug_printv("IStream: past fseek 2"); @@ -343,26 +343,6 @@ uint32_t TNFSIStream::read(uint8_t* buf, uint32_t size) { return bytesRead; }; - -uint32_t TNFSIStream::size() { - return _size; -}; - -uint32_t TNFSIStream::available() { - if(!isOpen()) return 0; - return _size - position(); -}; - - -uint32_t TNFSIStream::position() { - if(!isOpen()) return 0; - return ftell(handle->file_h); -}; - -size_t TNFSIStream::error() { - return 0; -}; - bool TNFSIStream::seek(uint32_t pos) { // Debug_printv("pos[%d]", pos); if (!isOpen()) { @@ -416,10 +396,9 @@ void TNFSHandle::obtain(std::string m_path, std::string mode) { // it will be caught by the real file open later on char *pathStr = new char[m_path.length()]; + strncpy(pathStr, m_path.data(), m_path.length()); if (pathStr) { - strncpy(pathStr, m_path.data(), m_path.length()); - // Make dirs up to the final fnamepart char *ptr = strchr(pathStr, '/'); while (ptr) { diff --git a/lib/meatloaf/network/tnfs.h b/lib/meatloaf/network/tnfs.h index 4e01e5586..682de0c82 100644 --- a/lib/meatloaf/network/tnfs.h +++ b/lib/meatloaf/network/tnfs.h @@ -28,7 +28,7 @@ friend class TNFSIStream; if (mstr::contains(name, "?") || mstr::contains(name, "*")) seekEntry( name ); - if (!pathValid(path)) + if (!pathValid(path.c_str())) m_isNull = true; else m_isNull = false; @@ -112,16 +112,9 @@ class TNFSIStream: public MStream { close(); } - // MStream methods bool isBrowsable() override { return false; }; bool isRandomAccess() override { return true; }; - // MStream methods - uint32_t available() override; - uint32_t size() override; - uint32_t position() override; - size_t error() override; - virtual bool seek(uint32_t pos) override; virtual bool seek(uint32_t pos, int mode) override; @@ -144,9 +137,6 @@ class TNFSIStream: public MStream { std::string localPath; std::unique_ptr handle; - -private: - size_t _size = 0; }; diff --git a/lib/meatloaf/service/cs.cpp b/lib/meatloaf/service/cs.cpp index f8a9ea0d9..95cee30c1 100644 --- a/lib/meatloaf/service/cs.cpp +++ b/lib/meatloaf/service/cs.cpp @@ -28,10 +28,11 @@ std::string CServerSessionMgr::readLn() { } bool CServerSessionMgr::sendCommand(std::string command) { + std::string c = mstr::toPETSCII2(command); // 13 (CR) sends the command if(establishSession()) { - Debug_printf("CServer: send command: %s\r\n", command.c_str()); - (*this) << (command+'\r'); + Serial.printf("CServer: send command: %s\r\n", c.c_str()); + (*this) << (c+'\r'); (*this).flush(); return true; } @@ -148,7 +149,7 @@ bool CServerIStream::open() { // name here MUST BE UPPER CASE // trim spaces from right of name too mstr::rtrimA0(file->name); - mstr::toPETSCII(file->name); + //mstr::toPETSCII2(file->name); CServerFileSystem::session.sendCommand("load "+file->name); // read first 2 bytes with size, low first, but may also reply with: ?500 - ERROR uint8_t buffer[2] = { 0, 0 }; @@ -162,7 +163,7 @@ bool CServerIStream::open() { else { m_bytesAvailable = buffer[0] + buffer[1]*256; // put len here // if everything was ok - Debug_printf("CServer: file open, size: %d\r\n", m_bytesAvailable); + Serial.printf("CServer: file open, size: %d\r\n", m_bytesAvailable); m_isOpen = true; } } diff --git a/lib/meatloaf/service/cs.h b/lib/meatloaf/service/cs.h index f7d6297ca..913e8ccb5 100644 --- a/lib/meatloaf/service/cs.h +++ b/lib/meatloaf/service/cs.h @@ -44,7 +44,7 @@ class csstreambuf : public std::streambuf { return true; int rc = m_wifi.open("commodoreserver.com", 1541); - Debug_printf("csstreambuf: connect to cserver returned: %d\r\n", rc); + Serial.printf("csstreambuf: connect to cserver returned: %d\r\n", rc); if(rc == 1) { if(gbuf == nullptr) @@ -59,7 +59,7 @@ class csstreambuf : public std::streambuf { } void close() { - Debug_printf("csstreambuf: closing\r\n"); + Serial.printf("csstreambuf: closing\r\n"); if(m_wifi.isOpen()) { m_wifi.close(); } @@ -221,10 +221,6 @@ class CServerFile: public MFile { MStream* createIStream(std::shared_ptr src) { return src.get(); }; MStream* meatStream() override ; // has to return OPENED stream - std::string petsciiName() override { - return name; - } - //MFile* cd(std::string newDir); bool isDirectory() override; bool rewindDirectory() override; diff --git a/lib/meatloaf/service/ml.h b/lib/meatloaf/service/ml.h index d06e2ff80..9a7d9b9ec 100644 --- a/lib/meatloaf/service/ml.h +++ b/lib/meatloaf/service/ml.h @@ -23,9 +23,10 @@ class MLFileSystem: public MFileSystem //Debug_printv("MLFileSystem::getFile(%s)", path.c_str()); PeoplesUrlParser urlParser; urlParser.parseUrl(path); + std::string code = mstr::toUTF8(urlParser.name); //Debug_printv("url[%s]", urlParser.name.c_str()); - std::string ml_url = "https://api.meatloaf.cc/?" + urlParser.name; + std::string ml_url = "https://api.meatloaf.cc/?" + code; //Debug_printv("ml_url[%s]", ml_url.c_str()); //Debug_printv("url[%s]", ml_url.c_str()); diff --git a/lib/meatloaf/tape/t64.cpp b/lib/meatloaf/tape/t64.cpp index 920a0f37c..93dab0235 100644 --- a/lib/meatloaf/tape/t64.cpp +++ b/lib/meatloaf/tape/t64.cpp @@ -18,7 +18,7 @@ bool T64IStream::seekEntry( std::string filename ) std::string entryFilename = entry.filename; mstr::rtrimA0(entryFilename); mstr::replaceAll(filename, "\\", "/"); - mstr::toASCII(entryFilename); + //mstr::toUTF8(entryFilename); Debug_printv("filename[%s] entry.filename[%.16s]", filename.c_str(), entryFilename.c_str()); // Read Entry From Stream @@ -41,11 +41,11 @@ bool T64IStream::seekEntry( std::string filename ) return false; } -bool T64IStream::seekEntry( size_t index ) +bool T64IStream::seekEntry( uint16_t index ) { // Calculate Sector offset & Entry offset index--; - uint8_t entryOffset = 0x40 + (index * sizeof(entry)); + uint16_t entryOffset = 0x40 + (index * sizeof(entry)); //Debug_printv("----------"); //Debug_printv("index[%d] sectorOffset[%d] entryOffset[%d] entry_index[%d]", index, sectorOffset, entryOffset, entry_index); @@ -64,8 +64,8 @@ bool T64IStream::seekEntry( size_t index ) } -size_t T64IStream::readFile(uint8_t* buf, size_t size) { - size_t bytesRead = 0; +uint16_t T64IStream::readFile(uint8_t* buf, uint16_t size) { + uint16_t bytesRead = 0; if (m_position < 2) { @@ -159,7 +159,7 @@ bool T64File::rewindDirectory() { media_blocks_free = 0; media_block_size = image->block_size; media_image = name; - mstr::toASCII(media_image); + //mstr::toUTF8(media_image); Debug_printv("media_header[%s] media_id[%s] media_blocks_free[%d] media_block_size[%d] media_image[%s]", media_header.c_str(), media_id.c_str(), media_blocks_free, media_block_size, media_image.c_str()); diff --git a/lib/meatloaf/tape/t64.h b/lib/meatloaf/tape/t64.h index 53f9170cc..2ce84833c 100644 --- a/lib/meatloaf/tape/t64.h +++ b/lib/meatloaf/tape/t64.h @@ -47,9 +47,9 @@ class T64IStream : public CBMImageStream { } bool seekEntry( std::string filename ) override; - bool seekEntry( size_t index ) override; + bool seekEntry( uint16_t index ) override; - size_t readFile(uint8_t* buf, size_t size) override; + uint16_t readFile(uint8_t* buf, uint16_t size) override; bool seekPath(std::string path) override; Header header; @@ -71,7 +71,7 @@ class T64File: public MFile { isDir = is_dir; media_image = name; - mstr::toASCII(media_image); + //mstr::toUTF8(media_image); }; ~T64File() { @@ -80,12 +80,6 @@ class T64File: public MFile { MStream* createIStream(std::shared_ptr containerIstream) override; - std::string petsciiName() override { - // It's already in PETSCII - mstr::replaceAll(name, "\\", "/"); - return name; - } - bool isDirectory() override; bool rewindDirectory() override; MFile* getNextFileInDir() override; diff --git a/lib/meatloaf/tape/tcrt.cpp b/lib/meatloaf/tape/tcrt.cpp index 46028c456..37fffbed5 100644 --- a/lib/meatloaf/tape/tcrt.cpp +++ b/lib/meatloaf/tape/tcrt.cpp @@ -20,7 +20,7 @@ bool TCRTIStream::seekEntry( std::string filename ) std::string entryFilename = entry.filename; mstr::rtrimA0(entryFilename); mstr::replaceAll(filename, "\\", "/"); - mstr::toASCII(entryFilename); + //mstr::toUTF8(entryFilename); Debug_printv("filename[%s] entry.filename[%.16s]", filename.c_str(), entryFilename.c_str()); // Read Entry From Stream @@ -43,11 +43,11 @@ bool TCRTIStream::seekEntry( std::string filename ) return false; } -bool TCRTIStream::seekEntry( size_t index ) +bool TCRTIStream::seekEntry( uint16_t index ) { // Calculate Sector offset & Entry offset index--; - size_t entryOffset = 0xE7 + (index * 32); + uint16_t entryOffset = 0xE7 + (index * 32); //Debug_printv("----------"); //Debug_printv("index[%d] entryOffset[%d] entry_index[%d]", (index + 1), entryOffset, entry_index); @@ -55,7 +55,11 @@ bool TCRTIStream::seekEntry( size_t index ) containerStream->seek(entryOffset); containerStream->read((uint8_t *)&entry, sizeof(entry)); - //Debug_printv("file_type[%02X] file_name[%.16s]", entry.file_type, entry.filename); + uint32_t file_start_address = (0xD8 + (entry.file_start_address[0] << 8 | entry.file_start_address[1])); + uint32_t file_size = (entry.file_size[0] << 8) | (entry.file_size[1] << 16) | (entry.file_size[2]); + uint32_t file_load_address = entry.file_load_address[0] | entry.file_load_address[1] << 8; + + Debug_printv("file_name[%.16s] file_type[%02X] data_offset[%X] file_size[%d] load_address[%04X]", entry.filename, entry.file_type, file_start_address, file_size, file_load_address); entry_index = index + 1; if ( entry.file_type == 0xFF ) @@ -64,8 +68,13 @@ bool TCRTIStream::seekEntry( size_t index ) return true; } -size_t TCRTIStream::readFile(uint8_t* buf, size_t size) { - size_t bytesRead = 0; +uint16_t TCRTIStream::readFile(uint8_t* buf, uint16_t size) { + uint16_t bytesRead = 0; + + if ( m_position == 0) + { + Debug_printv("send load address[%4X]", m_load_address); + } bytesRead += containerStream->read(buf, size); m_bytesAvailable -= bytesRead; @@ -84,15 +93,20 @@ bool TCRTIStream::seekPath(std::string path) { if ( seekEntry(path) ) { //auto entry = containerImage->entry; - auto type = decodeType(entry.file_type).c_str(); + auto type = decodeType(mapType(entry.file_type)).c_str(); Debug_printv("filename [%.16s] type[%s]", entry.filename, type); // Calculate file size - m_length = entry.file_size[0] + entry.file_size[0] + entry.file_size[0]; + m_length = (entry.file_size[0] << 8) | (entry.file_size[1] << 16) | (entry.file_size[2]); m_bytesAvailable = m_length; + // Load Address + m_load_address[0] = entry.file_load_address[0]; + m_load_address[1] = entry.file_load_address[1]; + // Set position to beginning of file - containerStream->seek(entry.data_offset); + uint32_t file_start_address = (0xD8 + (entry.file_start_address[0] << 8 | entry.file_start_address[1])); + containerStream->seek(file_start_address); Debug_printv("File Size: size[%d] available[%d]", m_length, m_bytesAvailable); @@ -138,12 +152,12 @@ bool TCRTFile::rewindDirectory() { image->seekHeader(); // Set Media Info Fields - media_header = mstr::format("%.24", image->header.disk_name); - media_id = "tcrt"; + media_header = mstr::format("%.16s", image->header.disk_name); + media_id = "TCRT"; media_blocks_free = 0; media_block_size = image->block_size; media_image = name; - mstr::toASCII(media_image); + //mstr::toUTF8(media_image); Debug_printv("media_header[%s] media_id[%s] media_blocks_free[%d] media_block_size[%d] media_image[%s]", media_header.c_str(), media_id.c_str(), media_blocks_free, media_block_size, media_image.c_str()); @@ -158,13 +172,19 @@ MFile* TCRTFile::getNextFileInDir() { // Get entry pointed to by containerStream auto image = ImageBroker::obtain(streamFile->url); - if ( image->seekNextImageEntry() ) + bool r = false; + do + { + r = image->seekNextImageEntry(); + } while ( r && image->mapType(image->entry.file_type) == 0x00); // Skip hidden files + + if ( r ) { std::string fileName = mstr::format("%.16s", image->entry.filename); mstr::replaceAll(fileName, "/", "\\"); //Debug_printv( "entry[%s]", (streamFile->url + "/" + fileName).c_str() ); auto file = MFSOwner::File(streamFile->url + "/" + fileName); - file->extension = image->decodeType(image->entry.file_type); + file->extension = image->decodeType(image->mapType(image->entry.file_type)); return file; } else @@ -179,12 +199,12 @@ MFile* TCRTFile::getNextFileInDir() { uint32_t TCRTFile::size() { // Debug_printv("[%s]", streamFile->url.c_str()); // use TCRT to get size of the file in image - auto image = ImageBroker::obtain(streamFile->url); + auto entry = ImageBroker::obtain(streamFile->url)->entry; //size_t blocks = (UINT16_FROM_LE_UINT16(image->entry.load_address) + image->entry.file_size)) / image->block_size; //size_t blocks = 1; - size_t blocks = ((image->entry.file_size[0] >> 24 & 0x000000FF) | (image->entry.file_size[1] >> 8 & 0x0000FF00) | (image->entry.file_size[2] << 8 & 0x00FF0000)) / image->block_size; + size_t bytes = (entry.file_size[0] << 8) | (entry.file_size[1] << 16) | (entry.file_size[2]); - return blocks; + return bytes; } diff --git a/lib/meatloaf/tape/tcrt.h b/lib/meatloaf/tape/tcrt.h index 0d5f8b620..5d9ae595d 100644 --- a/lib/meatloaf/tape/tcrt.h +++ b/lib/meatloaf/tape/tcrt.h @@ -29,9 +29,9 @@ class TCRTIStream : public CBMImageStream { struct Entry { char filename[16]; uint8_t file_type; - uint16_t data_offset; + uint8_t file_start_address[2]; // from tcrt file system at 0xD8 uint8_t file_size[3]; - uint16_t load_address; + uint8_t file_load_address[2]; uint16_t bundle_compatibility; uint16_t bundle_main_start; uint16_t bundle_main_length; @@ -39,7 +39,6 @@ class TCRTIStream : public CBMImageStream { }; void seekHeader() override { - Debug_printv("here"); containerStream->seek(0x18); containerStream->read((uint8_t*)&header, sizeof(header)); } @@ -49,14 +48,64 @@ class TCRTIStream : public CBMImageStream { } bool seekEntry( std::string filename ) override; - bool seekEntry( size_t index ) override; - - size_t readFile(uint8_t* buf, size_t size) override; + bool seekEntry( uint16_t index ) override; bool seekPath(std::string path) override; + uint16_t readFile(uint8_t* buf, uint16_t size) override; + Header header; Entry entry; + // Translate TCRT file type to standard CBM file type + uint8_t mapType(uint8_t file_type) + { + // Type + // 0x00 - 0x3f: An program which can be loaded into the C64 and executed. + // The load address from the FS is used (not stored in the file!). The load address + size must fit into the C64. + // 0x00: general program + // 0x01: game + // 0x02: utility + // 0x03: multimedia + // 0x04: demo + // 0x05: image + // 0x06: tune + // 0x38-0x3f: private use area + if ( file_type <= 0x3F ) + return 0x82; // PRG + + // 0x40 - 0x7f: A bundled file (see below) Same types as 0x00-0x3f (just for a bundle and not a prg) + // This mode is designed for games that need to load further files or store data like high scores or save games. + // It also cen be used for subdirectories. + if ( file_type >= 0x40 && file_type <= 0x7F ) + return 0x86; // DIR + + // 0x80 - 0xef: Data files may not displayed by a browser. + // 0x80: general data + // 0x81: text file (lower PETSCII) + // 0x82: koala image + // 0x83: hires image + // 0x84: fli image (multicolor) + if ( file_type == 0x81 ) + return 0x81; // SEQ + else if ( file_type >= 0x80 && file_type <= 0xEF ) + return 0x82; // PRG + + // 0xf0: separator - this name should be displayed just as a separator. size must be 0, all other info data (start, load address) is undefined + if ( file_type == 0xF0 ) + return 0x82; // PRG + + // 0xfe: System file! This file is (part of) the program that launches at power up. + // No other program should ever rename/delete this file or relocate the blocks. + // (The entry within the FS however can be moved around. This may cover the FS itself, but it's not required.) + //if ( file_type == 0xFE ) + // return 0x00; // DEL + + // 0xff: Marker for the first free entry. + + // All types not mentioned above are reserved. + return 0x00; // DEL + } + private: friend class TCRTFile; }; @@ -73,7 +122,7 @@ class TCRTFile: public MFile { isDir = is_dir; media_image = name; - mstr::toASCII(media_image); + //mstr::toUTF8(media_image); }; ~TCRTFile() { @@ -82,12 +131,6 @@ class TCRTFile: public MFile { MStream* createIStream(std::shared_ptr containerIstream) override; - std::string petsciiName() override { - // It's already in PETSCII - mstr::replaceAll(name, "\\", "/"); - return name; - } - bool isDirectory() override; bool rewindDirectory() override; MFile* getNextFileInDir() override; diff --git a/lib/meatloaf/wrappers/iec_buffer.cpp b/lib/meatloaf/wrappers/iec_buffer.cpp index 869ec8b70..ea424bd3f 100644 --- a/lib/meatloaf/wrappers/iec_buffer.cpp +++ b/lib/meatloaf/wrappers/iec_buffer.cpp @@ -24,7 +24,7 @@ size_t oiecstream::easyWrite() { // pptr = Returns the pointer to the current character (put pointer) in the put area. // pbase = Returns the pointer to the beginning ("base") of the put area. // epptr = Returns the pointer one past the end of the put area. - Debug_printv("buff->IEC:"); + Serial.printf("buff->IEC:"); for(auto b = pbase(); b < pptr()-1; b++) { //Serial.printf("%c",*b); //Serial.printf("%c[%.2X]",*b, *b); diff --git a/lib/utils/string_utils.cpp b/lib/utils/string_utils.cpp index 94670cca6..97d03ad1c 100644 --- a/lib/utils/string_utils.cpp +++ b/lib/utils/string_utils.cpp @@ -2,6 +2,7 @@ #include "../../include/petscii.h" #include "../../include/debug.h" +#include "U8Char.h" #include #include @@ -23,8 +24,6 @@ constexpr unsigned int hash(const char *s, int off = 0) { namespace mstr { - std::string byteSuffixes[9] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y" }; - // trim from start (in place) void ltrim(std::string &s) { @@ -216,18 +215,45 @@ namespace mstr { [](unsigned char c) { return std::toupper(c); }); } - // convert to ascii (in place) - void toASCII(std::string &s) + // // convert to ascii (in place) - DO NOT USE, use toUtf8 instead! + // void toASCII(std::string &s) + // { + // std::transform(s.begin(), s.end(), s.begin(), + // [](unsigned char c) { return petscii2ascii(c); }); + // } + + // // convert to petscii (in place) - DO NOT USE, utf8 can't be converted in place! + // void toPETSCII(std::string &s) + // { + // std::transform(s.begin(), s.end(), s.begin(), + // [](unsigned char c) { return ascii2petscii(c); }); + // } + + // convert PETSCII to UTF8, using methods from U8Char + std::string toUTF8(std::string &petsciiInput) { - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c) { return petscii2ascii(c); }); + std::string utf8string; + for(char petscii : petsciiInput) { + U8Char u8char(petscii); + utf8string+=u8char.toUtf8(); + } + return utf8string; } - // convert to petscii (in place) - void toPETSCII(std::string &s) + // convert UTF8 to PETSCII, using methods from U8Char + std::string toPETSCII2(std::string &utfInputString) { - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c) { return ascii2petscii(c); }); + std::string petsciiString; + char* utfInput = (char*)utfInputString.c_str(); + auto end = utfInput + utfInputString.length(); + + while(utfInput= 1 ); - n = value / std::pow(1024, --i); - asprintf(&f, "%.2f %s", n, byteSuffixes[i].c_str()); - return f; + n = size / std::pow(1024, --i); + return format("%.2f %s", n, byteSuffixes[i].c_str()); } diff --git a/lib/utils/string_utils.h b/lib/utils/string_utils.h index 0eb9841ae..76cfb3c6a 100644 --- a/lib/utils/string_utils.h +++ b/lib/utils/string_utils.h @@ -23,33 +23,46 @@ inline constexpr auto operator"" _sh(const char *str, size_t len) { namespace mstr { std::string drop(std::string str, size_t count); std::string dropLast(std::string str, size_t count); + bool startsWith(std::string s, const char *pattern, bool case_sensitive = true); bool endsWith(std::string s, const char *pattern, bool case_sensitive = true); + bool equals(std::string &s1, std::string &s2, bool case_sensitive = true); bool equals(std::string &s1, char *s2, bool case_sensitive = true); bool equals(const char* s1, const char *s2, bool case_sensitive); bool contains(std::string &s1, const char *s2, bool case_sensitive = true); bool compare(std::string &s1, std::string &s2, bool case_sensitive = true); // s1 is Wildcard string, s2 is potential match + std::vector split(std::string toSplit, char ch, int limit = 9999); void toLower(std::string &s); void toUpper(std::string &s); + void ltrim(std::string &s); void rtrim(std::string &s); void rtrimA0(std::string &s); void trim(std::string &s); + void replaceAll(std::string &s, const std::string &search, const std::string &replace); + std::string joinToString(std::vector::iterator* start, std::vector::iterator* end, std::string separator); std::string joinToString(std::vector, std::string separator); + std::string urlEncode(const std::string &s); std::string urlDecode(std::string s); - void toASCII(std::string &s); - void toPETSCII(std::string &s); + + // void toASCII(std::string &s); + // void toPETSCII(std::string &s); + std::string toUTF8(std::string &petsciiInput); + std::string toPETSCII2(std::string &utfInputString); + bool isText(std::string &s); bool isNumeric(std::string &s); bool isA0Space(int ch); void A02Space(std::string &s); + std::string format(const char *format, ...); std::string formatBytes(uint64_t value); + void cd(std::string &path, std::string newDir); std::string parent(std::string path, std::string plus = ""); std::string localParent(std::string path, std::string plus);