libMeatloaf/lib/meatloaf/wrappers/directory_stream.h
2024-01-08 11:58:15 -06:00

111 lines
4.3 KiB
C++

#ifndef MEATLOAF_WRAPPER_DIRECTORY_STREAM
#define MEATLOAF_WRAPPER_DIRECTORY_STREAM
#include <memory>
#include <fstream>
#include "meat_io.h"
class idirbuf : public std::filebuf {
const size_t BUFFER_SIZE = 256;
std::shared_ptr<MFile> 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<MFile> 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<MFile> 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<char>::eof();
}
return this->gptr() == this->egptr()
? std::char_traits<char>::eof()
: std::char_traits<char>::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<MFile> 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 */