diff --git a/build/libaegisub/libaegisub.vcxproj b/build/libaegisub/libaegisub.vcxproj
index 293c84a9f..a38601917 100644
--- a/build/libaegisub/libaegisub.vcxproj
+++ b/build/libaegisub/libaegisub.vcxproj
@@ -52,6 +52,7 @@
+
@@ -77,7 +78,6 @@
-
@@ -97,6 +97,7 @@
+
diff --git a/build/libaegisub/libaegisub.vcxproj.filters b/build/libaegisub/libaegisub.vcxproj.filters
index ff2bba3f6..0856f727c 100644
--- a/build/libaegisub/libaegisub.vcxproj.filters
+++ b/build/libaegisub/libaegisub.vcxproj.filters
@@ -80,9 +80,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -167,6 +164,9 @@
Header Files
+
+ Header Files
+
@@ -274,6 +274,9 @@
Source Files\Common
+
+ Source Files\Common
+
diff --git a/libaegisub/Makefile b/libaegisub/Makefile
index 245e1340b..48d2cdeb6 100644
--- a/libaegisub/Makefile
+++ b/libaegisub/Makefile
@@ -25,6 +25,7 @@ SRC += \
common/charset_conv.cpp \
common/color.cpp \
common/dispatch.cpp \
+ common/file_mapping.cpp \
common/fs.cpp \
common/hotkey.cpp \
common/io.cpp \
diff --git a/libaegisub/common/file_mapping.cpp b/libaegisub/common/file_mapping.cpp
new file mode 100644
index 000000000..d661e8e6a
--- /dev/null
+++ b/libaegisub/common/file_mapping.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2014, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include "../config.h"
+
+#include "libaegisub/file_mapping.h"
+
+#include "libaegisub/fs.h"
+#include "libaegisub/util.h"
+
+#include
+#include
+
+using namespace boost::interprocess;
+
+namespace agi {
+file_mapping::file_mapping(agi::fs::path const& filename, mode_t mode)
+#ifdef _WIN32
+: handle(CreateFileW(filename.wstring().c_str(), (unsigned int)mode, 0, nullptr, OPEN_EXISTING, 0, 0))
+{
+ if (handle == ipcdetail::invalid_file()) {
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ throw fs::FileNotFound(filename);
+ case ERROR_ACCESS_DENIED:
+ throw fs::ReadDenied(filename);
+ default:
+ throw fs::FileSystemUnknownError(util::ErrorString(GetLastError()));
+ }
+ }
+#else
+: handle(ipcdetail::open_existing_file(filename.string().c_str(), mode))
+{
+ if (handle == ipcdetail::invalid_file()) {
+ switch (errno) {
+ case ENOENT:
+ throw fs::FileNotFound(filename);
+ case EACCES:
+ throw fs::ReadDenied(filename);
+ case EIO:
+ throw fs::FileSystemUnknownError("Fatal I/O opening path: " + filename.string());
+ }
+ }
+#endif
+}
+
+file_mapping::~file_mapping() {
+ if (handle != ipcdetail::invalid_file()) {
+ ipcdetail::close_file(handle);
+ }
+}
+}
diff --git a/libaegisub/include/libaegisub/util_win.h b/libaegisub/include/libaegisub/file_mapping.h
similarity index 55%
rename from libaegisub/include/libaegisub/util_win.h
rename to libaegisub/include/libaegisub/file_mapping.h
index 2a90ba780..6de6d200c 100644
--- a/libaegisub/include/libaegisub/util_win.h
+++ b/libaegisub/include/libaegisub/file_mapping.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010, Amar Takhar
+// Copyright (c) 2014, Thomas Goyne
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -11,18 +11,23 @@
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
-/// @file util.h
-/// @brief Public interface for Windows utilities.
-/// @ingroup libaegisub windows
+#include
-#include
-
-#define WIN32_LEAN_AND_MEAN
-#include
+#include
namespace agi {
- namespace util {
- std::string ErrorString(DWORD error);
- } // namespace util
-} // namespace agi
+ // boost::interprocess::file_mapping is awesome and uses CreateFileA on Windows
+ class file_mapping {
+ boost::interprocess::file_handle_t handle;
+
+ public:
+ file_mapping(fs::path const& filename, boost::interprocess::mode_t mode);
+ ~file_mapping();
+ boost::interprocess::mapping_handle_t get_mapping_handle() const {
+ return boost::interprocess::ipcdetail::mapping_handle_from_file_handle(handle);
+ }
+ };
+}
diff --git a/libaegisub/include/libaegisub/util.h b/libaegisub/include/libaegisub/util.h
index bca64c108..ca92c3edd 100644
--- a/libaegisub/include/libaegisub/util.h
+++ b/libaegisub/include/libaegisub/util.h
@@ -72,5 +72,7 @@ namespace agi {
return std::any_of(std::begin(r), std::end(r), std::forward(p));
}
+ std::string ErrorString(int error);
+
} // namespace util
} // namespace agi
diff --git a/libaegisub/windows/access.cpp b/libaegisub/windows/access.cpp
index 62181d01b..9d42b64e7 100644
--- a/libaegisub/windows/access.cpp
+++ b/libaegisub/windows/access.cpp
@@ -22,7 +22,6 @@
#include
#include
#include
-#include
#include
#include
diff --git a/libaegisub/windows/fs.cpp b/libaegisub/windows/fs.cpp
index 6cff55cb6..a22d7556f 100644
--- a/libaegisub/windows/fs.cpp
+++ b/libaegisub/windows/fs.cpp
@@ -22,7 +22,7 @@
#include "libaegisub/charset_conv_win.h"
#include "libaegisub/exception.h"
#include "libaegisub/scoped_ptr.h"
-#include "libaegisub/util_win.h"
+#include "libaegisub/util.h"
using agi::charset::ConvertW;
using agi::charset::ConvertLocal;
@@ -30,6 +30,8 @@ using agi::charset::ConvertLocal;
#include
namespace bfs = boost::filesystem;
+#define WIN32_LEAN_AND_MEAN
+#include
#include
#undef CreateDirectory
diff --git a/libaegisub/windows/path_win.cpp b/libaegisub/windows/path_win.cpp
index a71231d55..470d7ec30 100644
--- a/libaegisub/windows/path_win.cpp
+++ b/libaegisub/windows/path_win.cpp
@@ -20,7 +20,7 @@
#include
-#include
+#include
#include
diff --git a/libaegisub/windows/util_win.cpp b/libaegisub/windows/util_win.cpp
index dbac2819d..3a6fa1e34 100644
--- a/libaegisub/windows/util_win.cpp
+++ b/libaegisub/windows/util_win.cpp
@@ -19,7 +19,6 @@
#include "../config.h"
#include "libaegisub/util.h"
-#include "libaegisub/util_win.h"
#include "libaegisub/charset_conv_win.h"
@@ -33,7 +32,7 @@ namespace agi {
using agi::charset::ConvertW;
-std::string ErrorString(DWORD error) {
+std::string ErrorString(int error) {
LPWSTR lpstr = nullptr;
if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, 0, reinterpret_cast(&lpstr), 0, nullptr) == 0) {
diff --git a/src/audio_provider_pcm.cpp b/src/audio_provider_pcm.cpp
index b01b18ab4..db6a3e63c 100644
--- a/src/audio_provider_pcm.cpp
+++ b/src/audio_provider_pcm.cpp
@@ -39,50 +39,21 @@
#include "audio_controller.h"
#include "utils.h"
+#include
#include
#include
#include
+#include
+#include
#include
#include
-#ifndef _WIN32
-#include
-#include
-#include
-#endif
+
+using namespace boost::interprocess;
PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename)
-#ifdef _WIN32
-: file_handle(0, CloseHandle)
-, file_mapping(0, CloseHandle)
+: file(agi::util::make_unique(filename, read_only))
{
- file_handle = CreateFile(
- filename.c_str(),
- FILE_READ_DATA,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- 0,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS,
- 0);
-
- if (file_handle == INVALID_HANDLE_VALUE)
- throw agi::fs::FileNotFound(filename);
-
- file_mapping = CreateFileMapping(
- file_handle,
- 0,
- PAGE_READONLY,
- 0, 0,
- 0);
-
- if (file_mapping == 0)
- throw agi::AudioProviderOpenError("Failed creating file mapping", 0);
-#else
-: file_handle(open(filename.c_str(), O_RDONLY), close)
-{
- if (file_handle == -1)
- throw agi::fs::FileNotFound(filename.string());
-#endif
float_samples = false;
try {
@@ -93,79 +64,36 @@ PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename)
}
}
-PCMAudioProvider::~PCMAudioProvider() {
-#ifdef _WIN32
- if (current_mapping)
- UnmapViewOfFile(current_mapping);
-#else
- if (current_mapping)
- munmap(current_mapping, mapping_length);
-#endif
-}
+PCMAudioProvider::~PCMAudioProvider() { }
-char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const {
- if (range_start + range_length > file_size)
+char *PCMAudioProvider::EnsureRangeAccessible(int64_t start, int64_t length) const {
+ if (start + length > file_size)
throw AudioDecodeError("Attempted to map beyond end of file");
- // Check whether the requested range is already visible
- if (!current_mapping || range_start < mapping_start || range_start+range_length > mapping_start+(int64_t)mapping_length) {
- // It's not visible, change the current mapping
- if (current_mapping) {
-#ifdef _WIN32
- UnmapViewOfFile(current_mapping);
-#else
- munmap(current_mapping, mapping_length);
-#endif
- }
+ // Check if we can just use the current mapping
+ if (region && start >= mapping_start && start + length <= mapping_start + region->get_size())
+ return static_cast(region->get_address()) + start - mapping_start;
- // Align range start on a 1 MB boundary and go 16 MB back
- mapping_start = (range_start & ~0xFFFFF) - 0x1000000;
- if (mapping_start < 0) mapping_start = 0;
-
- if (sizeof(void*) > 4)
- // Large address space, use a 2 GB mapping
- mapping_length = 0x80000000;
- else
- // Small (32 bit) address space, use a 256 MB mapping
- mapping_length = 0x10000000;
-
- // Make sure to always make a mapping at least as large as the requested range
- if ((int64_t)mapping_length < range_length) {
- if (range_length > (int64_t)(~(size_t)0))
- throw AudioDecodeError("Requested range larger than max size_t, cannot create view of file");
- mapping_length = range_length;
- }
- // But also make sure we don't try to make a mapping larger than the file
- if (mapping_start + (int64_t)mapping_length > file_size)
- mapping_length = (size_t)(file_size - mapping_start);
- // We already checked that the requested range doesn't extend over the end of the file
- // Hopefully this should ensure that small files are always mapped in their entirety
-
-#ifdef _WIN32
- LARGE_INTEGER mapping_start_li;
- mapping_start_li.QuadPart = mapping_start;
- current_mapping = MapViewOfFile(
- file_mapping, // Mapping handle
- FILE_MAP_READ, // Access type
- mapping_start_li.HighPart, // Offset high-part
- mapping_start_li.LowPart, // Offset low-part
- mapping_length); // Length of view
-#else
- current_mapping = mmap(nullptr, mapping_length, PROT_READ, MAP_PRIVATE, file_handle, mapping_start);
-#endif
-
- if (!current_mapping)
- throw AudioDecodeError("Failed mapping a view of the file");
+ if (sizeof(size_t) == 4) {
+ mapping_start = start & ~0xFFFFFULL; // Align to 1 MB bondary
+ length += static_cast(start - mapping_start);
+ // Map 16 MB or length rounded up to the next MB
+ length = std::min(std::max(0x1000000U, (length + 0xFFFFF) & ~0xFFFFF), file_size - mapping_start);
+ }
+ else {
+ // Just map the whole file
+ mapping_start = 0;
+ length = file_size;
}
- assert(current_mapping);
- assert(range_start >= mapping_start);
+ try {
+ region = agi::util::make_unique(*file, read_only, mapping_start, length);
+ }
+ catch (interprocess_exception const&) {
+ throw AudioDecodeError("Failed mapping a view of the file");
+ }
- // Difference between actual current mapping start and requested range start
- ptrdiff_t rel_ofs = (ptrdiff_t)(range_start - mapping_start);
-
- // Calculate a pointer into current mapping for the requested range
- return ((char*)current_mapping) + rel_ofs;
+ return static_cast(region->get_address()) + start - mapping_start;
}
void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
diff --git a/src/audio_provider_pcm.h b/src/audio_provider_pcm.h
index fbd4cbf11..4b2959ac2 100644
--- a/src/audio_provider_pcm.h
+++ b/src/audio_provider_pcm.h
@@ -27,42 +27,23 @@
//
// Aegisub Project http://www.aegisub.org/
-/// @file audio_provider_pcm.h
-/// @see audio_provider_pcm.cpp
-/// @ingroup audio_input
-///
-
-#include
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include
-#endif
-
#include "include/aegisub/audio_provider.h"
-#include
+#include
+#include
+
+namespace agi { class file_mapping; }
+namespace boost { namespace interprocess { class mapped_region; } }
class PCMAudioProvider : public AudioProvider {
- mutable void *current_mapping = nullptr;
-
-#ifdef _WIN32
+ std::unique_ptr file;
+ mutable std::unique_ptr region;
mutable int64_t mapping_start = 0;
- mutable size_t mapping_length = 0;
-
- agi::scoped_holder file_handle;
- agi::scoped_holder file_mapping;
-#else
- mutable off_t mapping_start = 0;
- mutable size_t mapping_length = 0;
-
- agi::scoped_holder file_handle;
-#endif
protected:
PCMAudioProvider(agi::fs::path const& filename); // Create base object and open the file mapping
- virtual ~PCMAudioProvider(); // Closes the file mapping
- char * EnsureRangeAccessible(int64_t range_start, int64_t range_length) const; // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range
+ ~PCMAudioProvider(); // Closes the file mapping
+ char *EnsureRangeAccessible(int64_t range_start, int64_t range_length) const; // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range
/// Size of the opened file
int64_t file_size = 0;