Skip to main content
Topic: using reader_mem with archive plugins (Read 2547 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

using reader_mem with archive plugins

Foobar2000 0.83

I've been trying to create an archive plugin for a custom music format (RWD). I've got to the point where the uncompressed music is in a memory buffer, so now all I need to do is create a reader_mem on the buffer. This process works and I can successfully use the reader to read the memory. However, the archive factory cannot seem to use it.

Whenever I add this archive to the playlist, I correctly get a list of all the tracks in the archive, but none of them get parsed, and all the playlist entries appear with "?" marks. In the file properties dialog box, only the packed path string appears. Nothing else is present (no meta info, no tech info, no song length, no file size). It seems as though Foobar2000 cannot use my reader.

How can I create a reader_mem that Foobar2000 can use to read archives?

Code: [Select]
reader *archive_rwd::get_reader(const char *path)
{
 /* Error handling removed for clarity. */
 string8 archive, file;
 RWDInfo rwd_data;

 this->parse_unpack_path(path, archive, file);
 reader *r_file = file::g_open_read(archive);

 /* RWD::ReadTrack fills rwd_data with useful metadata such as buffer sizes */
 RWD::ReadTrack(r_file, file, rwd_data);

 mem_block_t<char> out_buffer (rwd_data.fileLength);
 RWD::DecompressData(r_file, out_buffer);
 r->reader_release();

 reader_mem *rActual = new service_impl_p2_t<reader_mem, void *, int>
   (out_buffer, out_buffer.get_size());
 /* rActual is fine here. It now holds ownership of out_buffer. */

 return rActual; /* But Foobar2000 cannot use it */
}


I've tried making out_buffer and rActual global variables: that didn't help. I've tried using both new and malloc instead of mem_block_t, that made things worse. I've also tried reader_limited.

Edit:
The problem appears to be a bug with reader_mem itself. Right after calling get_reader for my archive plugin, Foobar2000 attempts to reader::open the path, and this is where the bug is.

The open method for reader_mem is a stub, and when Foobar2000 attempts to use the reader, it returns failure. That's why my playlist is coming up empty. To correct the bug:

Code: [Select]
// In "../SDK/reader_mem.h" line 68:
class reader_mem : public reader_mem_base
{
 virtual bool open(const char *filename, MODE mode)
 {
   return 0; /* <-- should be "return true;" */
   // return true; /* Use this line instead. */
 }//dummy
public:
 reader_mem(void * p_data,int p_size)
 {
   free_on_exit = true;
   mem_init(p_data,p_size);
 }
};


(The same code is used with reader_limited, which explains why it fails too) If I change the line in open(), then everything works fine. I suppose instead of hacking the headers, I could override the method in a derived class.

But won't it be considered a hack if I try to override this behaviour (whether through header hacking or derived class overrides)?

using reader_mem with archive plugins

Reply #1
Quote
The problem appears to be a bug with reader_mem itself. Right after calling get_reader for my archive plugin, Foobar2000 attempts to reader::open the path, and this is where the bug is.[a href="index.php?act=findpost&pid=327819"][{POST_SNAPBACK}][/a]

The reader_mem::open method bears the comment "// dummy" for a reason; it is not supposed to be called at all. Calling this method or passing a pointer to a reader_mem instance to code that will call it, is a bug in your code. (You can make it return false, if you don't like the 0 there.)

using reader_mem with archive plugins

Reply #2
Quote
The reader_mem::open method bears the comment "// dummy" for a reason; it is not supposed to be called at all.


It's not my code that's calling reader_mem::open, it's Foobar2000. If open is not supposed to be called, why is Foobar2000 calling it? And if half of the readers that implement the method return failure, why is Foobar2000 testing the return value of open?

Quote
Calling this method or passing a pointer to a reader_mem instance to code that will call it, is a bug in your code.


What does reader::open do anyway? All the readers in the SDK are dummies, and the comment for the function just says it's supposed to be called only once, which isn't very helpful. And why must Foobar2000 stop in event of it's failure?


I've already worked around this problem by creating a derived class that returns true for open, but this feels like a hack (what was that forum rule about posting hacked components)?

using reader_mem with archive plugins

Reply #3
From the 0.8.2 SDK (the latest, still valid for 0.8.3, emphasis added by me):
Quote
virtual reader * file::get_reader(const char * path)=0;//note: reader will usually stay alive after you are service_release'd; do not open the file at this point, just create a reader capable of opening this file and return it

So file::get_reader() should not open the file, that is the job of reader::open(). The same applies to archive_i::get_reader().

reader_mem and friends are helper classes. They do not implement reading of regular files, hence they cannot be opened and their open method is a dummy that always signals failure. However, it is perfectly legal to pass pointers to them to code that expects already opened readers.

using reader_mem with archive plugins

Reply #4
Basically, you need to implement the archive handling in your own custom reader class.

The archive service should only index the contents of the archive. If the format supports solid compression, it should also support precaching the tags from the entire archive through linear extraction of every file in the set.

The actual opening should happen in your custom reader service. All of the existing archivers derive from reader_mem_base, unpack the file to a mem_block(_t<char> or whatever) that exists within the context of the function, then mem_init( block.release(), size ).

mem_block[_t]::release() releases the pointer from the block, so it won't free it when it goes out of context, since reader_mem_base frees its own memory by default. See reader_helper_mem.h. ( You could also just as easily use malloc, but then you wouldn't get automatic cleanup on error returns, unless you fall through to a basic error return, and do the mem_init/sucess return inside of a deep if block nest. )

 
SimplePortal 1.0.0 RC1 © 2008-2020