Fix a typo
[alure.git] / examples / alure-physfs.cpp
blob7dbe37fd1141acdba62ad94d4a7aa83d5ba5afe5
1 #include <iostream>
2 #include <sstream>
3 #include <iomanip>
4 #include <cstring>
5 #include <limits>
6 #include <thread>
7 #include <chrono>
9 #include "physfs.h"
11 #include "alure2.h"
13 namespace
16 // Inherit from std::streambuf to handle custom I/O (PhysFS for this example)
17 class StreamBuf : public std::streambuf {
18 static const size_t sBufferSize = 4096;
20 PHYSFS_File *mFile;
21 char mBuffer[sBufferSize];
23 virtual int_type underflow()
25 if(mFile && gptr() == egptr())
27 // Read in the next chunk of data, and set the read pointers on
28 // success
29 PHYSFS_sint64 got = PHYSFS_read(mFile, mBuffer, sizeof(mBuffer[0]), sBufferSize);
30 if(got != -1) setg(mBuffer, mBuffer, mBuffer+got);
32 if(gptr() == egptr())
33 return traits_type::eof();
34 return (*gptr())&0xFF;
37 virtual pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode)
39 if(!mFile || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
40 return traits_type::eof();
42 // PhysFS only seeks using absolute offsets, so we have to convert cur-
43 // and end-relative offsets.
44 PHYSFS_sint64 fpos;
45 switch(whence)
47 case std::ios_base::beg:
48 break;
50 case std::ios_base::cur:
51 // Need to offset for the read pointers with std::ios_base::cur
52 // regardless
53 offset -= off_type(egptr()-gptr());
54 if((fpos=PHYSFS_tell(mFile)) == -1)
55 return traits_type::eof();
56 offset += fpos;
57 break;
59 case std::ios_base::end:
60 if((fpos=PHYSFS_fileLength(mFile)) == -1)
61 return traits_type::eof();
62 offset += fpos;
63 break;
65 default:
66 return traits_type::eof();
68 // Range check - absolute offsets cannot be less than 0, nor be greater
69 // than PhysFS's offset type.
70 if(offset < 0 || offset >= std::numeric_limits<PHYSFS_sint64>::max())
71 return traits_type::eof();
72 if(PHYSFS_seek(mFile, PHYSFS_sint64(offset)) == 0)
73 return traits_type::eof();
74 // Clear read pointers so underflow() gets called on the next read
75 // attempt.
76 setg(0, 0, 0);
77 return offset;
80 virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode)
82 // Simplified version of seekoff
83 if(!mFile || (mode&std::ios_base::out) || !(mode&std::ios_base::in))
84 return traits_type::eof();
86 if(pos < 0 || pos >= std::numeric_limits<PHYSFS_sint64>::max())
87 return traits_type::eof();
88 if(PHYSFS_seek(mFile, PHYSFS_sint64(pos)) == 0)
89 return traits_type::eof();
90 setg(0, 0, 0);
91 return pos;
94 public:
95 bool open(const char *filename)
97 mFile = PHYSFS_openRead(filename);
98 if(!mFile) return false;
99 return true;
102 StreamBuf() : mFile(nullptr)
104 virtual ~StreamBuf()
106 PHYSFS_close(mFile);
107 mFile = nullptr;
111 // Inherit from std::istream to use our custom streambuf
112 class Stream : public std::istream {
113 public:
114 Stream(const char *filename) : std::istream(new StreamBuf())
116 // Set the failbit if the file failed to open.
117 if(!(static_cast<StreamBuf*>(rdbuf())->open(filename)))
118 clear(failbit);
120 virtual ~Stream()
121 { delete rdbuf(); }
124 // Inherit from alure::FileIOFactory to use our custom istream
125 class FileFactory : public alure::FileIOFactory {
126 public:
127 FileFactory(const char *argv0)
129 // Need to initialize PhysFS before using it
130 if(PHYSFS_init(argv0) == 0)
132 std::stringstream sstr;
133 sstr<< "Failed to initialize PhysFS: "<<PHYSFS_getLastError();
134 throw std::runtime_error(sstr.str());
137 std::cout<< "Initialized PhysFS, supported archive formats:" <<std::endl;
138 for(const PHYSFS_ArchiveInfo **i = PHYSFS_supportedArchiveTypes();*i != NULL;i++)
139 std::cout<< " "<<(*i)->extension<<": "<<(*i)->description <<std::endl;
140 std::cout<<std::endl;
142 virtual ~FileFactory()
144 PHYSFS_deinit();
147 virtual alure::SharedPtr<std::istream> openFile(const alure::String &name)
149 alure::SharedPtr<Stream> stream(new Stream(name.c_str()));
150 if(stream->fail()) stream.reset();
151 return stream;
154 // A PhysFS-specific function to mount a new path to the virtual directory
155 // tree.
156 static bool Mount(const char *path, const char *mountPoint=nullptr, int append=0)
158 std::cout<< "Adding new file source "<<path;
159 if(mountPoint) std::cout<< " to "<<mountPoint;
160 std::cout<<"..."<<std::endl;
162 if(PHYSFS_mount(path, mountPoint, append) == 0)
164 std::cerr<< "Failed to add "<<path<<": "<<PHYSFS_getLastError() <<std::endl;
165 return false;
167 return true;
171 } // namespace
174 int main(int argc, char *argv[])
176 if(argc < 2)
178 std::cerr<< "Usage: "<<argv[0]<<" -add <directory | archive> file_entries ..." <<std::endl;
179 return 1;
182 // Set our custom factory for file IO (Alure holds a reference to the factory
183 // instance). From now on, all filenames given to Alure will be used with
184 // our custom factory.
185 alure::FileIOFactory::set(alure::SharedPtr<alure::FileIOFactory>(new FileFactory(argv[0])));
187 alure::DeviceManager *devMgr = alure::DeviceManager::get();
189 alure::Device *dev = devMgr->openPlayback();
190 std::cout<< "Opened \""<<dev->getName(alure::PlaybackDevType_Basic)<<"\"" <<std::endl;
192 alure::Context *ctx = dev->createContext();
193 alure::Context::MakeCurrent(ctx);
195 for(int i = 1;i < argc;i++)
197 if(strcasecmp(argv[i], "-add") == 0 && argc-i > 1)
199 FileFactory::Mount(argv[++i]);
200 continue;
203 alure::SharedPtr<alure::Decoder> decoder(ctx->createDecoder(argv[i]));
204 alure::Source *source = ctx->getSource();
205 source->play(decoder, 32768, 4);
206 std::cout<< "Playing "<<argv[i]<<" ("<<alure::GetSampleTypeName(decoder->getSampleType())<<", "
207 <<alure::GetChannelConfigName(decoder->getChannelConfig())<<", "
208 <<decoder->getFrequency()<<"hz)" <<std::endl;
210 float invfreq = 1.0f / decoder->getFrequency();
211 while(source->isPlaying())
213 std::cout<< "\r "<<std::setiosflags(std::ios::fixed)<<std::setprecision(2)<<
214 (source->getOffset()*invfreq)<<" / "<<(decoder->getLength()*invfreq);
215 std::cout.flush();
216 std::this_thread::sleep_for(std::chrono::milliseconds(25));
217 ctx->update();
219 std::cout<<std::endl;
221 source->release();
222 source = nullptr;
223 decoder.reset();
226 alure::Context::MakeCurrent(nullptr);
227 ctx->destroy();
228 ctx = 0;
229 dev->close();
230 dev = 0;
232 return 0;