aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/compat.h
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2018-11-04 15:24:24 -0800
committerChris Robinson <[email protected]>2018-11-04 15:24:24 -0800
commit4dafb7dab136e85ebf362a309dd4feabd1e3b1a1 (patch)
tree0d8504291a6b8ab98fafda66e5672079c2b0605a /Alc/compat.h
parent26f7007507b8a25cb56cb243c232f18ffc901a89 (diff)
Use C++ to read and parse ambdec files
Diffstat (limited to 'Alc/compat.h')
-rw-r--r--Alc/compat.h176
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 {