diff options
author | Chris Robinson <[email protected]> | 2018-11-04 15:24:24 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2018-11-04 15:24:24 -0800 |
commit | 4dafb7dab136e85ebf362a309dd4feabd1e3b1a1 (patch) | |
tree | 0d8504291a6b8ab98fafda66e5672079c2b0605a /Alc/compat.h | |
parent | 26f7007507b8a25cb56cb243c232f18ffc901a89 (diff) |
Use C++ to read and parse ambdec files
Diffstat (limited to 'Alc/compat.h')
-rw-r--r-- | Alc/compat.h | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/Alc/compat.h b/Alc/compat.h index c478c01d..1c348c34 100644 --- a/Alc/compat.h +++ b/Alc/compat.h @@ -22,7 +22,9 @@ FILE *al_fopen(const char *fname, const char *mode); #ifdef __cplusplus } // extern "C" +#include <array> #include <string> +#include <fstream> inline std::string wstr_to_utf8(const WCHAR *wstr) { @@ -39,6 +41,164 @@ inline std::string wstr_to_utf8(const WCHAR *wstr) return ret; } +inline std::wstring utf8_to_wstr(const char *str) +{ + std::wstring ret; + + int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + if(len > 0) + { + ret.resize(len); + MultiByteToWideChar(CP_UTF8, 0, str, -1, &ret[0], len); + ret.pop_back(); + } + + return ret; +} + + +namespace al { + +// Windows' std::ifstream fails with non-ANSI paths since the standard only +// specifies names using const char* (or std::string). MSVC has a non-standard +// extension using const wchar_t* (or std::wstring?) to handle Unicode paths, +// but not all Windows compilers support it. So we have to make our own istream +// that accepts UTF-8 paths and forwards to Unicode-aware I/O functions. +class filebuf final : public std::streambuf { + std::array<char_type,4096> mBuffer; + HANDLE mFile{INVALID_HANDLE_VALUE}; + + int_type underflow() override + { + if(mFile != INVALID_HANDLE_VALUE && gptr() == egptr()) + { + // Read in the next chunk of data, and set the pointers on success + DWORD got = 0; + if(ReadFile(mFile, mBuffer.data(), (DWORD)mBuffer.size(), &got, nullptr)) + setg(mBuffer.data(), mBuffer.data(), mBuffer.data()+got); + } + if(gptr() == egptr()) + return traits_type::eof(); + return traits_type::to_int_type(*gptr()); + } + + pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override + { + if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in)) + return traits_type::eof(); + + LARGE_INTEGER fpos; + switch(whence) + { + case std::ios_base::beg: + fpos.QuadPart = offset; + if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN)) + return traits_type::eof(); + break; + + case std::ios_base::cur: + // If the offset remains in the current buffer range, just + // update the pointer. + if((offset >= 0 && offset < off_type(egptr()-gptr())) || + (offset < 0 && -offset <= off_type(gptr()-eback()))) + { + // Get the current file offset to report the correct read + // offset. + fpos.QuadPart = 0; + if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT)) + return traits_type::eof(); + setg(eback(), gptr()+offset, egptr()); + return fpos.QuadPart - off_type(egptr()-gptr()); + } + // Need to offset for the file offset being at egptr() while + // the requested offset is relative to gptr(). + offset -= off_type(egptr()-gptr()); + fpos.QuadPart = offset; + if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_CURRENT)) + return traits_type::eof(); + break; + + case std::ios_base::end: + fpos.QuadPart = offset; + if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_END)) + return traits_type::eof(); + break; + + default: + return traits_type::eof(); + } + setg(nullptr, nullptr, nullptr); + return fpos.QuadPart; + } + + pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override + { + // Simplified version of seekoff + if(mFile == INVALID_HANDLE_VALUE || (mode&std::ios_base::out) || !(mode&std::ios_base::in)) + return traits_type::eof(); + + LARGE_INTEGER fpos; + fpos.QuadPart = pos; + if(!SetFilePointerEx(mFile, fpos, &fpos, FILE_BEGIN)) + return traits_type::eof(); + + setg(nullptr, nullptr, nullptr); + return fpos.QuadPart; + } + +public: + bool open(const wchar_t *filename) + { + mFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if(mFile == INVALID_HANDLE_VALUE) return false; + return true; + } + bool open(const char *filename) + { + std::wstring wname{utf8_to_wstr(filename)}; + return open(wname.c_str()); + } + + bool is_open() const noexcept { return mFile != INVALID_HANDLE_VALUE; } + + filebuf() = default; + ~filebuf() override + { + if(mFile != INVALID_HANDLE_VALUE) + CloseHandle(mFile); + mFile = INVALID_HANDLE_VALUE; + } +}; + +// Inherit from std::istream to use our custom streambuf +class ifstream final : public std::istream { + filebuf mStreamBuf; + +public: + ifstream(const std::wstring &filename) : ifstream{filename.c_str()} { } + ifstream(const wchar_t *filename) : std::istream{nullptr} + { + init(&mStreamBuf); + + // Set the failbit if the file failed to open. + if(!mStreamBuf.open(filename)) clear(failbit); + } + + ifstream(const std::string &filename) : ifstream{filename.c_str()} { } + ifstream(const char *filename) : std::istream{nullptr} + { + init(&mStreamBuf); + + // Set the failbit if the file failed to open. + if(!mStreamBuf.open(filename)) clear(failbit); + } + + bool is_open() const noexcept { return mStreamBuf.is_open(); } +}; + +} // namespace al + extern "C" { #endif /* __cplusplus */ @@ -50,6 +210,22 @@ extern "C" { #define HAVE_DYNLOAD 1 #endif + +#ifdef __cplusplus +} // extern "C" + +#include <fstream> + +namespace al { + +using filebuf = std::filebuf; +using ifstream = std::ifstream; + +} // namespace al + +extern "C" { +#endif /* __cplusplus */ + #endif struct FileMapping { |