#ifndef MEATLOAF_WRAPPER_DIRECTORY_STREAM #define MEATLOAF_WRAPPER_DIRECTORY_STREAM #include #include #include "meat_io.h" class idirbuf : public std::filebuf { const size_t BUFFER_SIZE = 256; std::shared_ptr container; char* buffer; uint8_t phase = 0; // 0 = headers, 1 = files, 2 = footer public: idirbuf() { buffer = new char[BUFFER_SIZE]; }; ~idirbuf() { if(buffer != nullptr) delete[] buffer; } void open(std::shared_ptr c) { container = c; container->rewindDirectory(); // in case we're in the middle of reading here! phase = 0; } // something will be READING from this stream, that's why we need to implement underflow int underflow() override { // it is up to us how we will fill this buffer. We can write it single line at // a time, or (if made big enough) with whole directory contents, it really // doesn't matter, because when everything from the buffer is read, underflow // will get called again // // I divided it into 3 phases, for header, files and footer, but again - any way is OK if(phase == 0) { // OK, so you are in headers. // First you have to fill this buffer line by line or as whole. Doesn't matter. // i.e. like this: size_t written = lineToBuffer(69,"some header line"); // When you're done, call this: this->setg(buffer, buffer, buffer + written); // of course filling it at once would be easiest, as you just fill it here and set this: //phase = 1; // if you fill line by line incremet some count and set phase to 1 when all lines written... } else if(phase == 1) { // So now we're in files list phase, let's try and get a file: std::unique_ptr entry(container->getNextFileInDir()); if (entry != nullptr) { // There's still a file in this dir, so... Same principle: // we'll put BASIV V2 into buffer variable using this function: auto readCount = fileToBasicV2(entry.get()); // and set required pointers: this->setg(buffer, buffer, buffer + readCount); // we're putting just this oone file in our buffer and exiting // underflow will be called to fill it with next file } else phase = 2; // nope, no more files here. Let's change phase, so we can write footer // in the same pass. Otherwise we'll get an EOF! } if(phase == 2) { // ok, so now the footer, again - write it into the buffer and call: size_t written = lineToBuffer(69,"bytes free"); this->setg(buffer, buffer, buffer + written); phase = 3; // next time underflow is called, we'll just return eof } else if(phase == 3) { // we've past the footer, let's send eof return std::char_traits::eof(); } return this->gptr() == this->egptr() ? std::char_traits::eof() : std::char_traits::to_int_type(*this->gptr()); }; size_t fileToBasicV2(MFile* file, long flags = 0L) { // convert MFile to ASCII line // we can use some additional FLAGS, i.e. for various CBM-style long directory format (they contain creation date and other info!) return lineToBuffer(69,"directory entry line in ASCII"); // return length of BASIC line written to the buffer } size_t lineToBuffer(uint16_t lineNumber, const std::string &lineContents) { // 1. convert lineContents to PETSCII or whatever is required to send to the C64 // 2. fill our buffer with next line pointer, line number and converted lineContents // for(i...) buffer[i]=XXXX... // 3. return how may bytes you put in the buffer: return 69; } }; // void exampleStreamFn(std::shared_ptr container) { // idirbuf dirbuffer; // dirbuffer.open(container); // std::istream dirStream(&dirbuffer); // // now you can just read BASIC V2 characters from dirStream // } #endif /* MEATLOAF_WRAPPER_DIRECTORY_STREAM */