Mostly purge wxWidgets from non-UI stuff

Use boost::filesystem::path for all paths, and std::string for all other
strings, converting to/from wxString as close to the actual uses of wx
as possible.

Where possible, replace the uses of non-UI wxWidgets functionality with
the additions to the standard library in C++11, or the equivalents in
boost.

Move the path token management logic to libaegisub (and rewrite it in
the process).

Add a basic thread pool based on asio and std::thread to libaegisub.

This touches nearly every file in the project and a nontrivial amount of
code had to be rewritten entirely, so there's probably a lot of broken
stuff.
This commit is contained in:
Thomas Goyne 2013-01-04 07:01:50 -08:00
parent 10e06ac3f9
commit 1e0f08c0ed
297 changed files with 5702 additions and 6573 deletions

View File

@ -134,7 +134,6 @@
<ClInclude Include="$(SrcDir)avisynth_wrap.h" />
<ClInclude Include="$(SrcDir)base_grid.h" />
<ClInclude Include="$(SrcDir)block_cache.h" />
<ClInclude Include="$(SrcDir)charset_conv.h" />
<ClInclude Include="$(SrcDir)charset_detect.h" />
<ClInclude Include="$(SrcDir)colorspace.h" />
<ClInclude Include="$(SrcDir)colour_button.h" />
@ -321,7 +320,6 @@
<ClCompile Include="$(SrcDir)auto4_lua_scriptreader.cpp" />
<ClCompile Include="$(SrcDir)avisynth_wrap.cpp" />
<ClCompile Include="$(SrcDir)base_grid.cpp" />
<ClCompile Include="$(SrcDir)charset_conv.cpp" />
<ClCompile Include="$(SrcDir)charset_detect.cpp" />
<ClCompile Include="$(SrcDir)colorspace.cpp" />
<ClCompile Include="$(SrcDir)colour_button.cpp" />

View File

@ -525,9 +525,6 @@
<ClInclude Include="$(SrcDir)charset_detect.h">
<Filter>Features\Import</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)charset_conv.h">
<Filter>Features\Import</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)help_button.h">
<Filter>Features\Help</Filter>
</ClInclude>
@ -1076,9 +1073,6 @@
<ClCompile Include="$(SrcDir)subs_grid.cpp">
<Filter>Main UI\Grid</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)charset_conv.cpp">
<Filter>Features\Import</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)charset_detect.cpp">
<Filter>Features\Import</Filter>
</ClCompile>

View File

@ -17,20 +17,27 @@
<!-- Project specific configuration -->
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(SrcDir);$(SrcDir)include;$(AegisubContribBase)iconv\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
$(SrcDir);
$(SrcDir)include;
$(AegisubContribBase)iconv\include;
%(AdditionalIncludeDirectories)
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>
NOMINMAX;
_WIN32_WINNT=0x0501;
%(PreprocessorDefinitions)
</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>lagi_pre.h</PrecompiledHeaderFile>
<ForcedIncludeFiles>lagi_pre.h</ForcedIncludeFiles>
</ClCompile>
</ItemDefinitionGroup>
<!-- Source files -->
<ItemGroup>
<ClInclude Include="$(SrcDir)lagi_pre.h" />
<ClInclude Include="$(SrcDir)config.h" />
<ClInclude Include="$(SrcDir)common\charset_6937.h" />
<ClInclude Include="$(SrcDir)common\charset_ucd.h" />
<ClInclude Include="$(SrcDir)common\option_visit.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\access.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\charset.h" />
@ -64,8 +71,13 @@
<ClInclude Include="$(SrcDir)include\libaegisub\background_runner.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\color.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\spellchecker.h" />
<ClInclude Include="..\..\libaegisub\include\libaegisub\calltip_provider.h" />
<ClInclude Include="..\..\libaegisub\include\libaegisub\of_type_adaptor.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\fs.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\of_type_adaptor.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\path.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\fs_fwd.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\dispatch.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\split.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
@ -78,7 +90,6 @@
<ClCompile Include="$(SrcDir)common\charset.cpp" />
<ClCompile Include="$(SrcDir)common\charset_6937.cpp" />
<ClCompile Include="$(SrcDir)common\charset_conv.cpp" />
<ClCompile Include="$(SrcDir)common\charset_ucd.cpp" />
<ClCompile Include="$(SrcDir)common\hotkey.cpp" />
<ClCompile Include="$(SrcDir)common\json.cpp" />
<ClCompile Include="$(SrcDir)common\keyframe.cpp" />
@ -96,8 +107,13 @@
<ClCompile Include="$(SrcDir)ass\dialogue_parser.cpp" />
<ClCompile Include="$(SrcDir)common\color.cpp" />
<ClCompile Include="$(SrcDir)common\parser.cpp" />
<ClCompile Include="..\..\libaegisub\common\calltip_provider.cpp" />
<ClCompile Include="..\..\libaegisub\common\io.cpp" />
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp" />
<ClCompile Include="$(SrcDir)common\fs.cpp" />
<ClCompile Include="$(SrcDir)common\io.cpp" />
<ClCompile Include="$(SrcDir)common\path.cpp" />
<ClCompile Include="$(SrcDir)windows\fs.cpp" />
<ClCompile Include="$(SrcDir)windows\path_win.cpp" />
<ClCompile Include="$(SrcDir)common\dispatch.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="$(SrcDir)include\libaegisub\charsets.def" />

View File

@ -26,9 +26,6 @@
<ClInclude Include="$(SrcDir)common\charset_6937.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)common\charset_ucd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)common\option_visit.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -134,10 +131,25 @@
<ClInclude Include="$(SrcDir)common\parser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\libaegisub\include\libaegisub\of_type_adaptor.h">
<ClInclude Include="$(SrcDir)include\libaegisub\of_type_adaptor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\libaegisub\include\libaegisub\calltip_provider.h">
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\path.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\fs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\fs_fwd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\dispatch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\split.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -160,9 +172,6 @@
<ClCompile Include="$(SrcDir)windows\charset_conv_win.cpp">
<Filter>Source Files\Windows</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\charset_ucd.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)windows\log_win.cpp">
<Filter>Source Files\Windows</Filter>
</ClCompile>
@ -220,10 +229,25 @@
<ClCompile Include="$(SrcDir)common\parser.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="..\..\libaegisub\common\io.cpp">
<ClCompile Include="$(SrcDir)common\io.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="..\..\libaegisub\common\calltip_provider.cpp">
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\path.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)windows\path_win.cpp">
<Filter>Source Files\Windows</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)windows\fs.cpp">
<Filter>Source Files\Windows</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\fs.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\dispatch.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
</ItemGroup>
@ -232,4 +256,4 @@
<Filter>Header Files</Filter>
</None>
</ItemGroup>
</Project>
</Project>

View File

@ -25,23 +25,27 @@ SRC += \
common/charset.cpp \
common/charset_6937.cpp \
common/charset_conv.cpp \
common/charset_ucd.cpp \
common/color.cpp \
common/dispatch.cpp \
common/fs.cpp \
common/hotkey.cpp \
common/io.cpp \
common/json.cpp \
common/keyframe.cpp \
common/log.cpp \
common/mru.cpp \
common/option.cpp \
common/option_visit.cpp \
common/parser.cpp \
common/util.cpp \
common/log.cpp \
common/path.cpp \
common/thesaurus.cpp \
common/util.cpp \
common/vfr.cpp \
unix/util.cpp \
unix/access.cpp \
unix/log.cpp
unix/fs.cpp \
unix/log.cpp \
unix/path.cpp \
unix/util.cpp
ifeq (yes, $(BUILD_DARWIN))
SRC += osx/util.mm

View File

@ -16,18 +16,118 @@
/// @brief Character set detection and manipulation utilities.
/// @ingroup libaegisub
#include "charset_ucd.h"
#include "libaegisub/charset.h"
namespace agi {
namespace charset {
#include "libaegisub/io.h"
std::string Detect(const std::string &file) {
return UCDetect(file).Single();
#include <fstream>
#include <string>
#ifndef _WIN32
#define _X86_ 1
#endif
#include "../../universalchardet/nscore.h"
#include "../../universalchardet/nsUniversalDetector.h"
#include "../../universalchardet/nsMBCSGroupProber.h"
#include "../../universalchardet/nsCharSetProber.h"
namespace {
using namespace agi::charset;
class UCDetect : public nsUniversalDetector {
/// List of detected character sets
CharsetListDetected list;
void Report(const char* aCharset) {}
public:
/// @brief Detect character set of a file using UniversalCharDetect
/// @param file File to check
UCDetect(agi::fs::path const& file)
: nsUniversalDetector(NS_FILTER_ALL)
{
{
std::unique_ptr<std::ifstream> fp(agi::io::Open(file, true));
// If it's over 100 MB it's either binary or big enough that we won't
// be able to do anything useful with it anyway
fp->seekg(0, std::ios::end);
if (fp->tellg() > 100 * 1024 * 1024) {
list.emplace_back(1.f, "binary");
return;
}
fp->seekg(0, std::ios::beg);
std::streamsize binaryish = 0;
std::streamsize bytes = 0;
while (!mDone && *fp) {
char buf[4096];
fp->read(buf, sizeof(buf));
std::streamsize read = fp->gcount();
HandleData(buf, (PRUint32)read);
// A dumb heuristic to detect binary files
if (!mDone) {
bytes += read;
for (std::streamsize i = 0; i < read; ++i) {
if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
++binaryish;
}
if (binaryish > bytes / 8) {
list.emplace_back(1.f, "binary");
return;
}
}
}
}
DataEnd();
if (mDetectedCharset)
list.emplace_back(1.f, mDetectedCharset);
else {
switch (mInputState) {
case eHighbyte: {
for (PRInt32 i=0; i<NUM_OF_CHARSET_PROBERS; i++) {
if (!mCharSetProbers[i]) continue;
float conf = mCharSetProbers[i]->GetConfidence();
if (conf > 0.01f)
list.emplace_back(conf, mCharSetProbers[i]->GetCharSetName());
}
break;
}
case ePureAscii:
list.emplace_back(1.f, "US-ASCII");
break;
default:
throw UnknownCharset("Unknown character set.");
}
if (list.empty() && (mInputState == eHighbyte))
throw UnknownCharset("Unknown character set.");
typedef std::pair<float, std::string> const& result;
sort(begin(list), end(list), [](result lft, result rgt) { return lft.first > rgt.first; });
}
}
/// @brief Detect character set of a file using UniversalCharDet
CharsetListDetected List() const { return list; }
};
}
CharsetListDetected DetectAll(const std::string& file) {
return UCDetect(file).List();
}
namespace agi { namespace charset {
std::string Detect(agi::fs::path const& file) {
return DetectAll(file).front().second;
}
} // namespace util
} // namespace agi
CharsetListDetected DetectAll(agi::fs::path const& file) {
return UCDetect(file).List();
}
} }

View File

@ -1,107 +0,0 @@
// Copyright (c) 2010, Amar Takhar <verm@aegisub.org>
//
// 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.
/// @file charset_ucd.cpp
/// @brief Character set detection using Universalchardet
/// @ingroup libaegisub
#include "charset_ucd.h"
#include "libaegisub/io.h"
#include "libaegisub/scoped_ptr.h"
#include "../../universalchardet/nsCharSetProber.h"
namespace agi {
namespace charset {
UCDetect::UCDetect(const std::string &file)
: nsUniversalDetector(NS_FILTER_ALL)
{
{
agi::scoped_ptr<std::ifstream> fp(io::Open(file, true));
// If it's over 100 MB it's either binary or big enough that we won't
// be able to do anything useful with it anyway
fp->seekg(0, std::ios::end);
if (fp->tellg() > 100 * 1024 * 1024) {
list.emplace_back(1.f, "binary");
return;
}
fp->seekg(0, std::ios::beg);
std::streamsize binaryish = 0;
std::streamsize bytes = 0;
while (!mDone && *fp) {
char buf[4096];
fp->read(buf, sizeof(buf));
std::streamsize read = fp->gcount();
HandleData(buf, (PRUint32)read);
// A dumb heuristic to detect binary files
if (!mDone) {
bytes += read;
for (std::streamsize i = 0; i < read; ++i) {
if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
++binaryish;
}
if (binaryish > bytes / 8) {
list.emplace_back(1.f, "binary");
return;
}
}
}
}
DataEnd();
if (mDetectedCharset)
list.emplace_back(1.f, mDetectedCharset);
else {
switch (mInputState) {
case eHighbyte: {
for (PRInt32 i=0; i<NUM_OF_CHARSET_PROBERS; i++) {
if (!mCharSetProbers[i]) continue;
float conf = mCharSetProbers[i]->GetConfidence();
if (conf > 0.01f)
list.emplace_back(conf, mCharSetProbers[i]->GetCharSetName());
}
break;
}
case ePureAscii:
list.emplace_back(1.f, "US-ASCII");
break;
default:
throw UnknownCharset("Unknown character set.");
}
if (list.empty() && (mInputState == eHighbyte))
throw UnknownCharset("Unknown character set.");
typedef std::pair<float, std::string> const& result;
sort(begin(list), end(list), [](result lft, result rgt) { return lft.first > rgt.first; });
}
}
std::string UCDetect::Single() const {
return list.front().second;
}
} // namespace util
} // namespace agi

View File

@ -1,56 +0,0 @@
// Copyright (c) 2010, Amar Takhar <verm@aegisub.org>
//
// 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.
/// @file charset_ucd.h
/// @brief Character set detection using Universalchardet
/// @ingroup libaegisub
#include "libaegisub/charset.h"
#include <string>
#ifndef _WIN32
#define _X86_ 1
#endif
#include "../../universalchardet/nscore.h"
#include "../../universalchardet/nsUniversalDetector.h"
#include "../../universalchardet/nsMBCSGroupProber.h"
namespace agi {
namespace charset {
class UCDetect : public nsUniversalDetector {
/// List of detected character sets.
CharsetListDetected list;
/// Stub.
void Report(const char* aCharset) {};
public:
/// @brief Detect character set of a file using UniversalCharDetect
/// @param file File to check
UCDetect(const std::string &file);
/// @brief Detect character set of a file using UniversalCharDet
CharsetListDetected List() const { return list; }
/// @brief Return a single character set (highest confidence)
/// @return Character set
std::string Single() const;
};
} // namespace util
} // namespace agi

View File

@ -0,0 +1,98 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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/dispatch.h"
#include <boost/asio/io_service.hpp>
#include <boost/asio/strand.hpp>
#include <condition_variable>
#include <mutex>
#include <thread>
namespace {
boost::asio::io_service *service;
std::function<void (agi::dispatch::Thunk)> invoke_main;
class MainQueue : public agi::dispatch::Queue {
void DoInvoke(agi::dispatch::Thunk thunk) {
invoke_main(thunk);
}
};
class BackgroundQueue : public agi::dispatch::Queue {
void DoInvoke(agi::dispatch::Thunk thunk) {
service->post(thunk);
}
};
class SerialQueue : public agi::dispatch::Queue {
boost::asio::io_service::strand strand;
void DoInvoke(agi::dispatch::Thunk thunk) {
strand.post(thunk);
}
public:
SerialQueue() : strand(*service) { }
};
}
namespace agi { namespace dispatch {
void Init(std::function<void (Thunk)> invoke_main) {
static boost::asio::io_service service;
static boost::asio::io_service::work work(service);
::service = &service;
::invoke_main = invoke_main;
for (unsigned i = 0; i < std::max<unsigned>(1, std::thread::hardware_concurrency()); ++i)
std::thread([]{ ::service->run(); }).detach();
}
void Queue::Async(Thunk thunk) {
DoInvoke(thunk);
}
void Queue::Sync(Thunk thunk) {
std::mutex m;
std::condition_variable cv;
std::unique_lock<std::mutex> l(m);
bool done = false;
DoInvoke([&]{
std::unique_lock<std::mutex> l(m);
thunk();
done = true;
cv.notify_all();
});
cv.wait(l, [&]{ return done; });
}
Queue& Main() {
static MainQueue q;
return q;
}
Queue& Background() {
static BackgroundQueue q;
return q;
}
std::unique_ptr<Queue> Create() {
return std::unique_ptr<Queue>(new SerialQueue);
}
} }

View File

@ -0,0 +1,106 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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/fs.h"
#include "libaegisub/access.h"
#include "libaegisub/exception.h"
#include "libaegisub/log.h"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
namespace bfs = boost::filesystem;
namespace ec = boost::system::errc;
// boost::filesystem functions throw a single exception type for all
// errors, which isn't really what we want, so do some crazy wrapper
// shit to map error codes to more useful exceptions.
#define CHECKED_CALL(exp, src_path, dst_path) \
boost::system::error_code ec; \
exp; \
switch (ec.value()) {\
case ec::success: break; \
case ec::no_such_file_or_directory: throw FileNotFound(src_path); \
case ec::is_a_directory: throw NotAFile(src_path); \
case ec::not_a_directory: throw NotADirectory(src_path); \
case ec::no_space_on_device: throw DriveFull(dst_path); \
case ec::permission_denied: \
if (!src_path.empty()) \
acs::CheckFileRead(src_path); \
if (!dst_path.empty()) \
acs::CheckFileWrite(dst_path); \
throw AccessDenied(src_path); \
default: \
LOG_D("filesystem") << "Unknown error when calling '" << #exp << "': " << ec << ": " << ec.message(); \
throw FileSystemUnknownError(ec.message()); \
}
#define CHECKED_CALL_RETURN(exp, src_path) \
CHECKED_CALL(auto ret = exp, src_path, agi::fs::path()); \
return ret
#define WRAP_BFS(bfs_name, agi_name) \
auto agi_name(path const& p) -> decltype(bfs::bfs_name(p)) { \
CHECKED_CALL_RETURN(bfs::bfs_name(p, ec), p); \
}
#define WRAP_BFS_IGNORE_ERROR(bfs_name, agi_name) \
auto agi_name(path const& p) -> decltype(bfs::bfs_name(p)) { \
boost::system::error_code ec; \
return bfs::bfs_name(p, ec); \
}
// sasuga windows.h
#undef CreateDirectory
namespace agi { namespace fs {
WRAP_BFS_IGNORE_ERROR(exists, Exists)
WRAP_BFS_IGNORE_ERROR(is_regular_file, FileExists)
WRAP_BFS_IGNORE_ERROR(is_directory, DirectoryExists)
WRAP_BFS(file_size, SizeImpl)
WRAP_BFS(last_write_time, ModifiedTime)
WRAP_BFS(create_directories, CreateDirectory)
WRAP_BFS(space, Space)
WRAP_BFS(remove, Remove)
WRAP_BFS(canonical, Canonicalize)
uintmax_t Size(path const& p) {
if (DirectoryExists(p))
throw NotAFile(p);
return SizeImpl(p);
}
uintmax_t FreeSpace(path const& p) {
return Space(p).available;
}
void Rename(const path& from, const path& to) {
CHECKED_CALL(bfs::rename(from, to, ec), from, to);
}
void Copy(path const& from, path const& to) {
CreateDirectory(to.parent_path());
CHECKED_CALL(bfs::copy_file(from, to, bfs::copy_option::overwrite_if_exists, ec), from, to);
}
bool HasExtension(path const& p, std::string const& ext) {
if (!p.has_extension()) return ext.empty();
return boost::iequals(p.extension().string().substr(1), ext);
}
} }

View File

@ -18,11 +18,6 @@
#include "../config.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <tuple>
#include "libaegisub/hotkey.h"
#include "libaegisub/cajun/writer.h"
@ -31,8 +26,12 @@
#include "libaegisub/json.h"
#include "libaegisub/log.h"
#include <algorithm>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/map.hpp>
#include <cmath>
#include <fstream>
#include <tuple>
namespace agi {
namespace hotkey {
@ -50,12 +49,12 @@ void Hotkey::ComboInsert(Combo const& combo) {
cmd_map.insert(make_pair(combo.CmdName(), combo));
}
Hotkey::Hotkey(const std::string &file, const std::string &default_config)
Hotkey::Hotkey(agi::fs::path const& file, const std::string &default_config)
: config_file(file)
{
LOG_D("hotkey/init") << "Generating hotkeys.";
json::Object object(agi::json_util::file(config_file, default_config));
const json::Object object(agi::json_util::file(config_file, default_config));
for (auto const& hotkey_context : object)
BuildHotkey(hotkey_context.first, hotkey_context.second);
}
@ -118,7 +117,7 @@ std::vector<std::string> Hotkey::GetHotkeys(const std::string &context, const st
for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) {
std::string ctext = it->second.Context();
if (ctext == "Always" || ctext == "Default" || ctext == context)
ret.push_back(it->second.StrMenu());
ret.emplace_back(it->second.StrMenu());
}
sort(ret.begin(), ret.end());

View File

@ -16,49 +16,25 @@
/// @brief Windows IO methods.
/// @ingroup libaegisub
#include <sys/stat.h>
#include <errno.h>
#include <iostream>
#include <fstream>
#include "libaegisub/io.h"
#include <libaegisub/access.h>
#include <libaegisub/charset_conv_win.h>
#include "libaegisub/io.h"
#include "libaegisub/fs.h"
#include "libaegisub/log.h"
#include "libaegisub/path.h"
#include "libaegisub/util.h"
#ifdef _WIN32
#define snprintf sprintf_s
#endif
namespace {
std::string make_temp_name(std::string const& filename) {
char tmp[1024];
snprintf(tmp, sizeof tmp, "_tmp_%lld", (long long)time(0));
std::string::size_type pos = filename.rfind('.');
if (pos == std::string::npos)
return filename + tmp;
return filename.substr(0, pos) + tmp + filename.substr(pos);
}
}
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
namespace agi {
namespace io {
using agi::charset::ConvertW;
#ifndef _WIN32
#define ConvertW
#endif
std::ifstream* Open(const std::string &file, bool binary) {
std::ifstream* Open(fs::path const& file, bool binary) {
LOG_D("agi/io/open/file") << file;
acs::CheckFileRead(file);
std::ifstream *stream = new std::ifstream(ConvertW(file).c_str(), (binary ? std::ios::binary : std::ios::in));
auto stream = new boost::filesystem::ifstream(file, (binary ? std::ios::binary : std::ios::in));
if (stream->fail()) {
delete stream;
@ -68,34 +44,30 @@ std::ifstream* Open(const std::string &file, bool binary) {
return stream;
}
Save::Save(const std::string& file, bool binary)
Save::Save(fs::path const& file, bool binary)
: file_name(file)
, tmp_name(make_temp_name(file))
, tmp_name(unique_path(file.parent_path()/(file.stem().string() + "_tmp_%%%%." + file.extension().string())))
{
LOG_D("agi/io/save/file") << file;
const std::string pwd = util::DirName(file);
acs::CheckDirWrite(pwd);
acs::CheckDirWrite(file.parent_path());
try {
acs::CheckFileWrite(file);
} catch (FileNotFoundError const&) {
// If the file doesn't exist we create a 0 byte file, this so so
// util::Rename will find it, and to let users know something went
// wrong by leaving a 0 byte file.
std::ofstream(ConvertW(file).c_str());
}
catch (fs::FileNotFound const&) {
// Not an error
}
fp = new std::ofstream(ConvertW(tmp_name).c_str(), binary ? std::ios::binary : std::ios::out);
fp = new boost::filesystem::ofstream(tmp_name, binary ? std::ios::binary : std::ios::out);
if (!fp->good()) {
delete fp;
throw agi::FileNotAccessibleError("Could not create temporary file at: " + tmp_name);
throw fs::WriteDenied(tmp_name);
}
}
Save::~Save() {
delete fp;
util::Rename(tmp_name, file_name);
delete fp; // Explicitly delete to unlock file on Windows
fs::Rename(tmp_name, file_name);
}
std::ofstream& Save::Get() {

View File

@ -18,21 +18,22 @@
#include "../config.h"
#include "libaegisub/json.h"
#include <fstream>
#include <memory>
#include <sstream>
#include "libaegisub/fs.h"
#include "libaegisub/io.h"
#include "libaegisub/json.h"
#include "libaegisub/log.h"
#include "libaegisub/scoped_ptr.h"
namespace agi {
namespace json_util {
json::UnknownElement parse(std::istream *stream) {
try {
agi::scoped_ptr<std::istream> stream_deleter(stream);
std::unique_ptr<std::istream> stream_deleter(stream);
json::UnknownElement root;
json::Reader::Read(root, *stream);
@ -46,15 +47,15 @@ json::UnknownElement parse(std::istream *stream) {
}
}
json::UnknownElement file(const std::string &file) {
json::UnknownElement file(agi::fs::path const& file) {
return parse(io::Open(file));
}
json::UnknownElement file(const std::string &file, const std::string &default_config) {
json::UnknownElement file(agi::fs::path const& file, const std::string &default_config) {
try {
return parse(io::Open(file));
}
catch (FileNotFoundError const&) {
catch (fs::FileNotFound const&) {
// Not an error
return parse(new std::istringstream(default_config));
}

View File

@ -17,16 +17,15 @@
/// @ingroup libaegisub
///
#include "../config.h"
#include "libaegisub/keyframe.h"
#include <algorithm>
#include <fstream>
#include "libaegisub/io.h"
#include "libaegisub/line_iterator.h"
#include "libaegisub/keyframe.h"
#include "libaegisub/vfr.h"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/algorithm.hpp>
@ -77,7 +76,7 @@ char x264(std::string const& line) {
}
namespace agi { namespace keyframe {
void Save(std::string const& filename, std::vector<int> const& keyframes) {
void Save(agi::fs::path const& filename, std::vector<int> const& keyframes) {
io::Save file(filename);
std::ofstream& of = file.Get();
of << "# keyframe format v1" << std::endl;
@ -85,7 +84,7 @@ void Save(std::string const& filename, std::vector<int> const& keyframes) {
boost::copy(keyframes, std::ostream_iterator<int>(of, "\n"));
}
std::vector<int> Load(std::string const& filename) {
std::vector<int> Load(agi::fs::path const& filename) {
std::unique_ptr<std::ifstream> file(io::Open(filename));
std::istream &is(*file);

View File

@ -18,21 +18,20 @@
#include "../config.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <functional>
#include <memory>
#include <sstream>
#include "libaegisub/log.h"
#include "libaegisub/cajun/elements.h"
#include "libaegisub/cajun/writer.h"
#include "libaegisub/io.h"
#include "libaegisub/log.h"
#include "libaegisub/types.h"
#include "libaegisub/util.h"
#include <algorithm>
#include <boost/range/algorithm.hpp>
#include <cstring>
#include <fstream>
#include <functional>
#include <memory>
namespace agi {
namespace log {
@ -109,7 +108,7 @@ Message::~Message() {
agi::log::log->log(sm);
}
JsonEmitter::JsonEmitter(std::string const& directory, const agi::log::LogSink *log_sink)
JsonEmitter::JsonEmitter(agi::fs::path const& directory, const agi::log::LogSink *log_sink)
: directory(directory)
, log_sink(log_sink)
{
@ -146,9 +145,7 @@ JsonEmitter::~JsonEmitter() {
timeval_close.push_back((int64_t)time_close.tv_sec);
timeval_close.push_back((int64_t)time_close.tv_usec);
std::stringstream str;
str << directory << time_start.tv_sec << ".json";
json::Writer::Write(root, io::Save(str.str()).Get());
json::Writer::Write(root, io::Save(directory/(std::to_string(time_start.tv_sec) + ".json")).Get());
}
} // namespace log

View File

@ -30,7 +30,7 @@
namespace agi {
MRUManager::MRUManager(std::string const& config, std::string const& default_config, agi::Options *options)
MRUManager::MRUManager(agi::fs::path const& config, std::string const& default_config, agi::Options *options)
: config_name(config)
, options(options)
{
@ -60,16 +60,22 @@ MRUManager::MRUListMap &MRUManager::Find(std::string const& key) {
return index->second;
}
void MRUManager::Add(std::string const& key, std::string const& entry) {
void MRUManager::Add(std::string const& key, agi::fs::path const& entry) {
MRUListMap &map = Find(key);
map.remove(entry);
map.push_front(entry);
Prune(key, map);
auto it = find(begin(map), end(map), entry);
if (it == begin(map) && it != end(map))
return;
if (it != end(map))
map.splice(begin(map), map, it);
else {
map.push_front(entry);
Prune(key, map);
}
Flush();
}
void MRUManager::Remove(std::string const& key, std::string const& entry) {
void MRUManager::Remove(std::string const& key, agi::fs::path const& entry) {
Find(key).remove(entry);
Flush();
@ -79,7 +85,7 @@ const MRUManager::MRUListMap* MRUManager::Get(std::string const& key) {
return &Find(key);
}
std::string const& MRUManager::GetEntry(std::string const& key, const size_t entry) {
agi::fs::path const& MRUManager::GetEntry(std::string const& key, const size_t entry) {
const MRUManager::MRUListMap *const map = Get(key);
if (entry >= map->size())
@ -93,7 +99,8 @@ void MRUManager::Flush() {
for (auto const& mru_map : mru) {
json::Array &array = out[mru_map.first];
array.insert(array.end(), mru_map.second.begin(), mru_map.second.end());
transform(begin(mru_map.second), end(mru_map.second),
back_inserter(array), [](agi::fs::path const& p) { return p.string(); });
}
json::Writer::Write(out, io::Save(config_name).Get());
@ -116,7 +123,8 @@ void MRUManager::Prune(std::string const& key, MRUListMap& map) const {
/// @param array json::Array of values.
void MRUManager::Load(std::string const& key, const json::Array& array) {
try {
copy(array.begin(), array.end(), back_inserter(mru[key]));
transform(begin(array), end(array),
back_inserter(mru[key]), [](std::string const& s) { return s; });
}
catch (json::Exception const&) {
// Out of date MRU file; just discard the data and skip it

View File

@ -20,14 +20,11 @@
#include "libaegisub/option.h"
#include <cassert>
#include <memory>
#include <sstream>
#include "libaegisub/cajun/reader.h"
#include "libaegisub/cajun/writer.h"
#include "libaegisub/cajun/elements.h"
#include "libaegisub/fs.h"
#include "libaegisub/io.h"
#include "libaegisub/log.h"
#include "libaegisub/option_value.h"
@ -35,6 +32,9 @@
#include "option_visit.h"
#include <boost/range/adaptor/map.hpp>
#include <cassert>
#include <memory>
#include <sstream>
namespace {
/// @brief Write an option to a json object
@ -66,7 +66,7 @@ namespace {
namespace agi {
Options::Options(const std::string &file, const std::string& default_config, const OptionSetting setting)
Options::Options(agi::fs::path const& file, const std::string& default_config, const OptionSetting setting)
: config_file(file)
, setting(setting)
{
@ -88,21 +88,17 @@ void Options::ConfigNext(std::istream& stream) {
}
void Options::ConfigUser() {
std::unique_ptr<std::istream> stream;
try {
stream.reset(agi::io::Open(config_file));
} catch (const FileNotFoundError&) {
std::unique_ptr<std::istream> stream(io::Open(config_file));
LoadConfig(*stream, true);
}
catch (fs::FileNotFound const&) {
return;
}
/// @todo Handle other errors such as parsing and notifying the user.
LoadConfig(*stream, true);
}
void Options::LoadConfig(std::istream& stream, bool ignore_errors) {
/// @todo Store all previously loaded configs in an array for bug report purposes,
/// this is just a temp stub.
json::UnknownElement config_root;
try {

View File

@ -0,0 +1,150 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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/
/// @file path.cpp
/// @brief Platform-independent path code
/// @ingroup libaegisub
#include "../config.h"
#include "libaegisub/path.h"
#include "libaegisub/fs.h"
#include "libaegisub/util.h"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/range/distance.hpp>
namespace {
template<class T, class U>
typename T::const_iterator last_less_than(T const& cont, U const& value) {
auto it = cont.upper_bound(value);
if (it != cont.begin())
--it;
return it;
}
}
namespace agi {
Path::Path() {
tokens["?user"];
tokens["?local"];
tokens["?data"];
tokens["?temp"];
tokens["?dictionary"];
tokens["?docs"];
FillPlatformSpecificPaths();
tokens["?audio"];
tokens["?script"];
tokens["?video"];
}
fs::path Path::Decode(std::string const& path) const {
const auto it = last_less_than(tokens, path);
if (!it->second.empty() && boost::starts_with(path, it->first))
return (it->second/path.substr(it->first.size())).make_preferred();
return fs::path(path).make_preferred();
}
fs::path Path::MakeRelative(fs::path const& path, std::string const& token) const {
const auto it = tokens.find(token);
if (it == tokens.end()) throw agi::InternalError("Bad token: " + token, 0);
return MakeRelative(path, it->second);
}
fs::path Path::MakeRelative(fs::path const& path, fs::path const& base) const {
if (path.empty() || base.empty()) return path;
const auto str = path.string();
if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:"))
return path;
// Paths on different volumes can't be made relative to each other
if (path.has_root_name() && path.root_name() != base.root_name())
return path.string();
auto path_it = path.begin();
auto ref_it = base.begin();
for (; path_it != path.end() && ref_it != base.end() && *path_it == *ref_it; ++path_it, ++ref_it) ;
agi::fs::path result;
for (; ref_it != base.end(); ++ref_it)
result /= "..";
for (; path_it != path.end(); ++path_it)
result /= *path_it;
return result;
}
fs::path Path::MakeAbsolute(fs::path path, std::string const& token) const {
const auto it = tokens.find(token);
if (it == tokens.end()) throw agi::InternalError("Bad token: " + token, 0);
if (path.empty()) return path;
path.make_preferred();
const auto str = path.string();
if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:"))
return path;
return (it->second.empty() || path.is_absolute()) ? path : it->second/path;
}
std::string Path::Encode(fs::path const& path) const {
// Find the shortest encoding of path made relative to each token
std::string shortest = path.string();
size_t length = boost::distance(path);
for (auto const& tok : tokens) {
if (tok.second.empty()) continue;
const auto p = MakeRelative(path, tok.first);
const size_t d = boost::distance(p);
if (d < length) {
length = d;
shortest = (tok.first/p).string();
}
}
return shortest;
}
void Path::SetToken(std::string const& token_name, fs::path const& token_value) {
const auto it = tokens.find(token_name);
if (it == tokens.end()) throw agi::InternalError("Bad token: " + token_name, 0);
if (token_value.empty())
it->second = token_value;
else if (!token_value.is_absolute())
it->second.clear();
else {
it->second = token_value;
it->second.make_preferred();
if (fs::FileExists(it->second))
it->second = it->second.parent_path();
}
paths.clear();
for (auto const& tok : tokens) {
if (!tok.second.empty())
paths[tok.second] = tok.first;
}
}
}

View File

@ -26,16 +26,16 @@
#include <boost/phoenix/operator/comparison.hpp>
#include <boost/phoenix/core/argument.hpp>
#include <cstdlib>
#include <fstream>
using boost::phoenix::placeholders::_1;
namespace agi {
Thesaurus::Thesaurus(std::string const& dat_path, std::string const& idx_path)
Thesaurus::Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path)
: dat(io::Open(dat_path))
{
scoped_ptr<std::ifstream> idx(io::Open(idx_path));
std::unique_ptr<std::ifstream> idx(io::Open(idx_path));
std::string encoding_name;
getline(*idx, encoding_name);

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011, Amar Takhar <verm@aegisub.org>
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@ -11,36 +11,26 @@
// 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.cpp
/// @brief Unix utility methods.
/// @ingroup libaegisub
#include <errno.h>
#include <climits>
#include <cstdio>
#include "../config.h"
#include "libaegisub/util.h"
#include <boost/algorithm/string/case_conv.hpp>
#include <ctime>
namespace agi {
namespace util {
namespace agi { namespace util {
void str_lower(std::string &str) {
boost::to_lower(str);
std::string strftime(const char *fmt, const tm *tmptr) {
if (!tmptr) {
time_t t = time(nullptr);
tmptr = localtime(&t);
}
char buff[65536];
::strftime(buff, sizeof buff, fmt, tmptr);
return buff;
}
int strtoi(std::string const& str) {
errno = 0;
long l = strtol(str.c_str(), nullptr, 10);
if ((errno == ERANGE) || (l < INT_MIN) || (l > INT_MAX))
return 0;
return (int)l;
}
} // namespace util
} // namespace agi
} }

View File

@ -16,10 +16,13 @@
/// @brief Framerate handling of all sorts
/// @ingroup libaegisub video_input
#include "../config.h"
#include "libaegisub/vfr.h"
#include <algorithm>
#include <cmath>
#include <fstream>
#include <functional>
#include <iterator>
#include <list>
@ -199,7 +202,7 @@ Framerate &Framerate::operator=(double fps) {
return *this = Framerate(fps);
}
Framerate::Framerate(std::string const& filename)
Framerate::Framerate(fs::path const& filename)
: denominator(default_denominator)
, numerator(0)
{
@ -221,7 +224,7 @@ Framerate::Framerate(std::string const& filename)
throw UnknownFormat(line);
}
void Framerate::Save(std::string const& filename, int length) const {
void Framerate::Save(fs::path const& filename, int length) const {
agi::io::Save file(filename);
std::ofstream &out = file.Get();

View File

@ -17,17 +17,11 @@
/// @ingroup libaegisub
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
namespace agi {
namespace acs {
DEFINE_SIMPLE_EXCEPTION_NOINNER(Fatal, FileSystemError, "filesystem/fatal");
DEFINE_SIMPLE_EXCEPTION_NOINNER(NotAFile, FileSystemError, "filesystem/not_a_file")
DEFINE_SIMPLE_EXCEPTION_NOINNER(NotADirectory, FileSystemError, "filesystem/not_a_directory")
DEFINE_SIMPLE_EXCEPTION_NOINNER(Read, FileNotAccessibleError, "filesystem/not_accessible/read_permission")
DEFINE_SIMPLE_EXCEPTION_NOINNER(Write, FileNotAccessibleError, "filesystem/not_accessible/write_permission")
enum Type {
FileRead,
DirRead,
@ -35,13 +29,13 @@ enum Type {
DirWrite
};
void Check(const std::string &file, acs::Type);
void Check(fs::path const& file, acs::Type);
void CheckFileRead(const std::string &file);
void CheckDirRead(const std::string &dir);
inline void CheckFileRead(fs::path const& file) { Check(file, acs::FileRead); }
inline void CheckFileWrite(fs::path const& file) { Check(file, acs::FileWrite); }
void CheckFileWrite(const std::string &file);
void CheckDirWrite(const std::string &dir);
inline void CheckDirRead(fs::path const& dir) { Check(dir, acs::DirRead); }
inline void CheckDirWrite(fs::path const& dir) { Check(dir, acs::DirWrite); }
} // namespace axs
} // namespace agi

View File

@ -16,11 +16,11 @@
/// @brief Character set detection and manipulation utilities.
/// @ingroup libaegisub
#include <fstream>
#include <functional>
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
#include <string>
#include <vector>
#include <libaegisub/exception.h>
namespace agi {
/// Character set conversion and detection.
@ -35,12 +35,12 @@ typedef std::vector<std::pair<float, std::string>> CharsetListDetected;
/// @brief Return a complete list of detected character sets ordered by precedence.
/// @param file File to check
/// @return List of possible charsets sorted by probability
CharsetListDetected DetectAll(std::string const& file);
CharsetListDetected DetectAll(agi::fs::path const& file);
/// @brief Returns the character set with the highest confidence
/// @param file File to check
/// @return Detected character set.
std::string Detect(const std::string &file);
std::string Detect(agi::fs::path const& file);
} // namespace util
} // namespace agi

View File

@ -23,5 +23,8 @@ namespace agi {
/// Convert a UTF-8 string to a string suitable for use with Win32 API functions
std::wstring ConvertW(std::string const& src);
std::string ConvertW(std::wstring const& src);
/// Convert a UTF-16 string to the local charset
std::string ConvertLocal(std::wstring const& src);
}
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <functional>
#include <memory>
namespace agi {
namespace dispatch {
typedef std::function<void()> Thunk;
class Queue {
virtual void DoInvoke(Thunk thunk)=0;
public:
virtual ~Queue() { }
/// Invoke the thunk on this processing queue, returning immediately
void Async(Thunk thunk);
/// Invoke the thunk on this processing queue, returning only when
/// it's complete
void Sync(Thunk thunk);
};
/// Initialize the dispatch thread pools
/// @param invoke_main A function which invokes the thunk on the GUI thread
void Init(std::function<void (Thunk)> invoke_main);
/// Get the main queue, which runs on the GUI thread
Queue& Main();
/// Get the generic background queue, which runs thunks in parallel
Queue& Background();
/// Create a new serial queue
std::unique_ptr<Queue> Create();
}
}

View File

@ -37,10 +37,7 @@
#include <memory>
#include <string>
/// @see aegisub.h
namespace agi {
/// @class Exception
/// @brief Base class for all exceptions in Aegisub.
///
@ -90,7 +87,6 @@ namespace agi {
/// could not be opened for reading". This is the purpose of the "inner"
/// exceptions.
class Exception {
/// The error message
std::string message;
@ -98,16 +94,13 @@ namespace agi {
std::shared_ptr<Exception> inner;
protected:
/// @brief Protected constructor initialising members
/// @param msg The error message
/// @param inr The inner exception, optional
///
/// Deriving classes should always use this constructor for initialising
/// the base class.
Exception(const std::string &msg, const Exception *inr = 0)
: message(msg)
{
Exception(const std::string &msg, const Exception *inr = 0) : message(msg) {
if (inr)
inner.reset(inr->Copy());
}
@ -120,9 +113,7 @@ namespace agi {
public:
/// @brief Destructor
virtual ~Exception()
{
}
virtual ~Exception() { }
/// @brief Get the outer exception error message
/// @return Error message
@ -147,7 +138,6 @@ namespace agi {
/// level are [a-z0-9_].
virtual const char * GetName() const = 0;
/// @brief Convert to char array as the error message
/// @return The error message
operator const char * () { return GetMessage().c_str(); }
@ -164,16 +154,12 @@ namespace agi {
virtual Exception *Copy() const = 0;
};
/// @brief Convenience macro to include the current location in code
///
/// Intended for use in error messages where it can sometimes be convenient to
/// indicate the exact position the error occurred at.
#define AG_WHERE " (at " __FILE__ ":" #__LINE__ ")"
/// @brief Convenience macro for declaring exceptions with no support for inner exception
/// @param classname Name of the exception class to declare
/// @param baseclass Class to derive from
@ -231,7 +217,6 @@ namespace agi {
classname(const std::string &msg, Exception *inner) : baseclass(msg, inner) { } \
};
/// @class agi::UserCancelException
/// @extends agi::Exception
/// @brief Exception for "user cancel" events
@ -246,7 +231,6 @@ namespace agi {
/// code in question.
DEFINE_SIMPLE_EXCEPTION_NOINNER(UserCancelException,Exception,"nonerror/user_cancel")
/// @class agi::InternalError
/// @extends agi:Exception
/// @brief Errors that should never happen and point to some invalid assumption in the code
@ -258,42 +242,17 @@ namespace agi {
/// and eventually cause an abort().
DEFINE_SIMPLE_EXCEPTION(InternalError, Exception, "internal_error")
/// @class agi::FileSystemError
/// @extends agi::Exception
/// @brief Base class for errors related to the file system
/// @class agi::EnvironmentError
/// @extends agi:Exception
/// @brief The execution environment is broken in some fundamental way
///
/// This base class can not be instantiated.
/// File system errors do not support inner exceptions, as they are always originating
/// causes for errors.
DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError,Exception)
/// @class agi::FileNotAccessibleError
/// @extends agi::FileSystemError
/// @brief A file can't be accessed for some reason
DEFINE_SIMPLE_EXCEPTION_NOINNER(FileNotAccessibleError,FileSystemError,"filesystem/not_accessible")
/// @class FileNotFoundError
/// @brief A file can't be accessed because there's no file by the given name
class FileNotFoundError : public FileNotAccessibleError {
public:
/// @brief Constructor, automatically builds the error message
/// @param filename Name of the file that could not be found
FileNotFoundError(const std::string &filename) : FileNotAccessibleError(std::string("File not found: ") + filename) { }
// Not documented, see agi::Exception class
const char * GetName() const { return "filesystem/not_accessible/not_found"; }
// Not documented, see agi::Exception class
Exception * Copy() const { return new FileNotFoundError(*this); }
};
/// Throw an environment error when a call to the platform API has failed
/// in some way that should normally never happen or suggests that the
/// runtime environment is too insane to support.
DEFINE_SIMPLE_EXCEPTION_NOINNER(EnvironmentError, Exception, "environment_error")
/// @class agi::InvalidInputException
/// @extends agi::Exception
/// @brief Some input data were invalid and could not be processed
DEFINE_BASE_EXCEPTION(InvalidInputException,Exception)
DEFINE_BASE_EXCEPTION(InvalidInputException, Exception)
}

View File

@ -0,0 +1,177 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
#include <boost/filesystem/path.hpp>
#include <cstdint>
#include <ctime>
#include <iterator>
#include <memory>
#include <string>
#undef CreateDirectory
namespace agi {
namespace fs {
/// Define a filesystem error which takes a path or a string
#define DEFINE_FS_EXCEPTION(type, base, message) \
struct type : public base { \
type(path const& p) : base(message + p.string()) { } \
type(std::string const& s) : base(s) { } \
const char *GetName() const { return ""; } \
Exception *Copy() const { return new type(*this); } \
}
/// @class agi::FileSystemError
/// @extends agi::Exception
/// @brief Base class for errors related to the file system
///
/// This base class can not be instantiated.
/// File system errors do not support inner exceptions, as they
/// are always originating causes for errors.
DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError, Exception)
/// A file can't be accessed for some reason
DEFINE_FS_EXCEPTION(FileNotAccessible, FileSystemError, "File is not accessible: ");
/// A file can't be accessed because there's no file by the given name
DEFINE_FS_EXCEPTION(FileNotFound, FileNotAccessible, "File not found: ");
/// An error of some unknown type has occured
DEFINE_SIMPLE_EXCEPTION_NOINNER(FileSystemUnknownError, FileSystemError, "filesystem/unknown");
/// The path exists, but isn't a file
DEFINE_FS_EXCEPTION(NotAFile, FileNotAccessible, "Path is not a file (and should be): ");
/// The path exists, but isn't a directory
DEFINE_FS_EXCEPTION(NotADirectory, FileNotAccessible, "Path is not a directory (and should be): ");
/// The given path is too long for the filesystem
DEFINE_FS_EXCEPTION(PathTooLog, FileSystemError, "Path is too long: ");
/// Insufficient free space to complete operation
DEFINE_FS_EXCEPTION(DriveFull, FileSystemError, "Insufficient free space to write file: ");
/// Base class for access denied errors
DEFINE_FS_EXCEPTION(AccessDenied, FileNotAccessible, "Access denied to path: ");
/// Trying to read the file gave an access denied error
DEFINE_FS_EXCEPTION(ReadDenied, AccessDenied, "Access denied when trying to read: ");
/// Trying to write the file gave an access denied error
DEFINE_FS_EXCEPTION(WriteDenied, AccessDenied, "Access denied when trying to write: ");
/// File exists and cannot be overwritten due to being read-only
DEFINE_FS_EXCEPTION(ReadOnlyFile, WriteDenied, "File is read-only: ");
bool Exists(path const& p);
bool FileExists(path const& file);
bool DirectoryExists(path const& dir);
/// Get the local-charset encoded shortname for a file
///
/// This is purely for compatibility with external libraries which do
/// not support unicode filenames on Windows. On all other platforms,
/// it is a no-op.
std::string ShortName(path const& file_path);
/// Check for amount of free space on a path
uintmax_t FreeSpace(path const& dir_path);
/// Get the size in bytes of the file at path
///
/// @throws agi::FileNotFound if path does not exist
/// @throws agi::acs::NotAFile if path is a directory
/// @throws agi::acs::Read if path exists but could not be read
uintmax_t Size(path const& file_path);
/// Get the modification time of the file at path
///
/// @throws agi::FileNotFound if path does not exist
/// @throws agi::acs::NotAFile if path is a directory
/// @throws agi::acs::Read if path exists but could not be read
time_t ModifiedTime(path const& file_path);
/// Create a directory and all required intermediate directories
/// @throws agi::acs::Write if the directory could not be created.
///
/// Trying to create a directory which already exists is not an error.
bool CreateDirectory(path const& dir_path);
/// Touch the given path
///
/// Creates the file if it does not exist, or updates the modified
/// time if it does
void Touch(path const& file_path);
/// Rename a file or directory
/// @param from Source path
/// @param to Destination path
void Rename(path const& from, path const& to);
/// Copy a file
/// @param from Source path
/// @param to Destination path
///
/// The destination path will be created if it does not exist.
void Copy(path const& from, path const& to);
/// Delete a file
/// @param path Path to file to delete
/// @throws agi::FileNotAccessibleError if file exists but could not be deleted
bool Remove(path const& file);
/// Check if the file has the given extension
/// @param p Path to check
/// @param ext Case-insensitive extension, without leading dot
bool HasExtension(path const& p, std::string const& ext);
agi::fs::path Canonicalize(agi::fs::path const& path);
class DirectoryIterator {
struct PrivData;
std::shared_ptr<PrivData> privdata;
std::string value;
public:
typedef path value_type;
typedef path* pointer;
typedef path& reference;
typedef size_t difference_type;
typedef std::forward_iterator_tag iterator_category;
bool operator==(DirectoryIterator const&) const;
bool operator!=(DirectoryIterator const& rhs) const { return !(*this == rhs); }
DirectoryIterator& operator++();
std::string const& operator*() const { return value; }
DirectoryIterator(path const& p, std::string const& filter);
DirectoryIterator();
~DirectoryIterator();
template<typename T> void GetAll(T& cont);
};
inline DirectoryIterator& begin(DirectoryIterator &it) { return it; }
inline DirectoryIterator end(DirectoryIterator &) { return DirectoryIterator(); }
template<typename T>
inline void DirectoryIterator::GetAll(T& cont) {
copy(*this, end(*this), std::back_inserter(cont));
}
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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/
namespace boost { namespace filesystem { class path; } }
namespace agi { namespace fs { typedef boost::filesystem::path path; } }

View File

@ -16,11 +16,13 @@
/// @brief Hotkey handler
/// @ingroup hotkey menu event window
#include <boost/filesystem/path.hpp>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <libaegisub/fs_fwd.h>
#include <libaegisub/signal.h>
namespace json {
@ -75,9 +77,9 @@ public:
/// Map to hold Combo instances
typedef std::multimap<std::string, Combo> HotkeyMap;
private:
HotkeyMap str_map; ///< String representation -> Combo
HotkeyMap cmd_map; ///< Command name -> Combo
const std::string config_file; ///< Default user config location.
HotkeyMap str_map; ///< String representation -> Combo
HotkeyMap cmd_map; ///< Command name -> Combo
const agi::fs::path config_file; ///< Default user config location.
/// Build hotkey map.
/// @param context Context being parsed.
@ -97,7 +99,7 @@ public:
/// Constructor
/// @param file Location of user config file.
/// @param default_config Default config.
Hotkey(const std::string &file, const std::string &default_config);
Hotkey(agi::fs::path const& file, const std::string &default_config);
/// Scan for a matching key.
/// @param context Context requested.

View File

@ -16,10 +16,11 @@
/// @brief Public interface for IO methods.
/// @ingroup libaegisub
#include <string>
#include <fstream>
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
#include <boost/filesystem/path.hpp>
#include <iosfwd>
namespace agi {
namespace io {
@ -27,15 +28,15 @@ namespace agi {
DEFINE_BASE_EXCEPTION_NOINNER(IOError, Exception)
DEFINE_SIMPLE_EXCEPTION_NOINNER(IOFatal, IOError, "io/fatal")
std::ifstream* Open(const std::string &file, bool binary = false);
std::ifstream* Open(fs::path const& file, bool binary = false);
class Save {
std::ofstream *fp;
const std::string file_name;
const std::string tmp_name;
const fs::path file_name;
const fs::path tmp_name;
public:
Save(const std::string& file, bool binary = false);
Save(fs::path const& file, bool binary = false);
~Save();
std::ofstream& Get();
};

View File

@ -18,6 +18,7 @@
#include <libaegisub/cajun/reader.h>
#include <libaegisub/cajun/elements.h>
#include <libaegisub/fs_fwd.h>
namespace agi {
namespace json_util {
@ -30,14 +31,13 @@ json::UnknownElement parse(std::istream *stream);
/// Parse a JSON file.
/// @param file Path JSON to file
/// @return json::UnknownElement
json::UnknownElement file(const std::string &file);
json::UnknownElement file(agi::fs::path const& file);
/// Parse a json stream, with default handler.
/// @param file Path to JSON file.
/// @param Default config file to load incase of nonexistent file
/// @return json::UnknownElement
json::UnknownElement file(const std::string &file, const std::string &default_config);
json::UnknownElement file(agi::fs::path const& file, const std::string &default_config);
} // namespace json_util
} // namespace agi

View File

@ -20,6 +20,7 @@
#include <vector>
#include "exception.h"
#include "fs_fwd.h"
namespace agi {
namespace vfr { class Framerate; }
@ -27,11 +28,12 @@ namespace agi {
/// @brief Load a keyframe file
/// @param filename File to load
/// @return List of frame numbers which are keyframes
std::vector<int> Load(std::string const& filename);
std::vector<int> Load(agi::fs::path const& filename);
/// @brief Save keyframes to a file
/// @param filename File to save to
/// @param keyframes List of keyframes to save
void Save(std::string const& filename, std::vector<int> const& keyframes);
void Save(agi::fs::path const& filename, std::vector<int> const& keyframes);
DEFINE_SIMPLE_EXCEPTION_NOINNER(Error, Exception, "keyframe/error")
}

View File

@ -146,6 +146,13 @@ public:
}
};
// Enable range-based for
template<typename T>
line_iterator<T>& begin(line_iterator<T>& it) { return it; }
template<typename T>
line_iterator<T> end(line_iterator<T>&) { return agi::line_iterator<T>(); }
template<class OutputType>
void line_iterator<OutputType>::getline(std::string &str) {
union {

View File

@ -16,12 +16,17 @@
/// @brief Logging
/// @ingroup libaegisub
#include <cstdint>
#include <libaegisub/fs_fwd.h>
#include <libaegisub/types.h>
#include <boost/filesystem/path.hpp>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <deque>
#include <memory>
#include <vector>
#ifdef __DEPRECATED // Dodge GCC warnings
# undef __DEPRECATED
# include <strstream>
@ -29,8 +34,6 @@
#else
# include <strstream>
#endif
#include <vector>
#include <libaegisub/types.h>
// These macros below aren't a perm solution, it will depend on how annoying they are through
// actual usage, and also depends on msvc support.
@ -144,7 +147,7 @@ class JsonEmitter : public Emitter {
agi_timeval time_start;
/// Directory to write the log file in
std::string directory;
agi::fs::path directory;
/// Parent sink to get messages from
const agi::log::LogSink *log_sink;
@ -152,7 +155,7 @@ public:
/// Constructor
/// @param directory Directory to write the log file in
/// @param log_sink Parent sink to get messages from
JsonEmitter(std::string const& directory, const agi::log::LogSink *log_sink);
JsonEmitter(agi::fs::path const& directory, const agi::log::LogSink *log_sink);
/// Destructor
~JsonEmitter();

View File

@ -16,12 +16,13 @@
/// @brief Public interface for MRU (Most Recently Used) lists.
/// @ingroup libaegisub
#include <boost/filesystem/path.hpp>
#include <deque>
#include <fstream>
#include <list>
#include <map>
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
namespace json {
class UnknownElement;
@ -49,11 +50,11 @@ DEFINE_SIMPLE_EXCEPTION_NOINNER(MRUErrorIndexOutOfRange, MRUError, "mru/invalid"
class MRUManager {
public:
/// @brief Map for time->value pairs.
typedef std::list<std::string> MRUListMap;
typedef std::list<agi::fs::path> MRUListMap;
/// @brief Constructor
/// @param config File to load MRU values from
MRUManager(std::string const& config, std::string const& default_config, agi::Options *options = 0);
MRUManager(agi::fs::path const& config, std::string const& default_config, agi::Options *options = 0);
/// Destructor
~MRUManager();
@ -62,13 +63,13 @@ public:
/// @param key List name
/// @param entry Entry to add
/// @exception MRUErrorInvalidKey thrown when an invalid key is used.
void Add(std::string const& key, std::string const& entry);
void Add(std::string const& key, agi::fs::path const& entry);
/// @brief Remove entry from the list.
/// @param key List name
/// @param entry Entry to add
/// @exception MRUErrorInvalidKey thrown when an invalid key is used.
void Remove(std::string const& key, std::string const& entry);
void Remove(std::string const& key, agi::fs::path const& entry);
/// @brief Return list
/// @param key List name
@ -79,14 +80,14 @@ public:
/// @param key List name
/// @param entry 0-base position of entry
/// @exception MRUErrorInvalidKey thrown when an invalid key is used.
std::string const& GetEntry(std::string const& key, const size_t entry);
agi::fs::path const& GetEntry(std::string const& key, const size_t entry);
/// Write MRU lists to disk.
void Flush();
private:
/// Internal name of the config file, set during object construction.
const std::string config_name;
const agi::fs::path config_name;
/// User preferences object for maximum number of items to list
agi::Options *const options;

View File

@ -18,10 +18,12 @@
#pragma once
#include <fstream>
#include <boost/filesystem/path.hpp>
#include <iosfwd>
#include <map>
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
namespace json {
class UnknownElement;
@ -60,7 +62,7 @@ private:
OptionValueMap values;
/// User config (file that will be written to disk)
const std::string config_file;
const agi::fs::path config_file;
/// Settings.
const OptionSetting setting;
@ -74,7 +76,7 @@ public:
/// @brief Constructor
/// @param file User config that will be loaded from and written back to.
/// @param default_config Default configuration.
Options(const std::string &file, const std::string &default_config, const OptionSetting setting=NONE);
Options(agi::fs::path const& file, const std::string &default_config, const OptionSetting setting=NONE);
/// Destructor
~Options();

View File

@ -0,0 +1,81 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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/
/// @file path.h
/// @brief Common paths.
/// @ingroup libaegisub
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
#include <boost/filesystem/path.hpp>
#include <map>
namespace agi {
/// Class for handling everything path-related in Aegisub
class Path {
/// Token -> Path map
std::map<std::string, fs::path> tokens;
/// Path -> Token map
std::map<fs::path, std::string> paths;
/// Platform-specific code to fill in the default paths, called in the constructor
void FillPlatformSpecificPaths();
public:
/// Constructor
Path();
/// Decode and normalize a path which may begin with a registered token
/// @param path Path which is either already absolute or begins with a token
/// @return Absolute path
fs::path Decode(std::string const& path) const;
/// If path is relative, make it absolute relative to the token's path
/// @param path A possibly relative path
/// @param token Token containing base path for resolving relative paths
/// @return Absolute path if `path` is absolute or `token` is set, `path` otherwise
/// @throws InternalError if `token` is not a valid token name
fs::path MakeAbsolute(fs::path path, std::string const& token) const;
/// If `token` is set, make `path` relative to it
/// @param path An absolute path
/// @param token Token name to make `path` relative to
/// @return A path relative to `token`'s value if `token` is set, `path` otherwise
/// @throws InternalError if `token` is not a valid token name
fs::path MakeRelative(fs::path const& path, std::string const& token) const;
fs::path MakeRelative(fs::path const& path, const char *token) const { return MakeRelative(path, std::string(token)); }
/// Make `path` relative to `base`, if possible
/// @param path An absolute path
/// @param base Base path to make `path` relative to
/// @return A path relative to `base`'s value if possible, or `path` otherwise
fs::path MakeRelative(fs::path const& path, fs::path const& base) const;
/// Encode an absolute path to begin with a token if there are any applicable
/// @param path Absolute path to encode
/// @return path untouched, or with some portion of the beginning replaced with a token
std::string Encode(fs::path const& path) const;
/// Set a prefix token to use for encoding and decoding paths
/// @param token_name A single word token beginning with '?'
/// @param token_value An absolute path to a directory or file
/// @throws InternalError if `token` is not a valid token name
void SetToken(std::string const& token_name, fs::path const& token_value);
};
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <boost/algorithm/string/finder.hpp>
#include <boost/algorithm/string/split.hpp>
namespace agi {
typedef boost::iterator_range<std::string::const_iterator> StringRange;
template<typename Str, typename Char>
boost::split_iterator<typename Str::const_iterator> Split(Str const& str, Char delim) {
return boost::make_split_iterator(str, boost::token_finder([=](Char c) { return c == delim; }));
}
inline std::string str(StringRange const& r) {
return std::string(r.begin(), r.end());
}
}
namespace boost {
namespace algorithm {
template<typename Iterator>
split_iterator<Iterator> begin(split_iterator<Iterator> it) {
return it;
}
template<typename Iterator>
split_iterator<Iterator> end(split_iterator<Iterator>) {
return split_iterator<Iterator>();
}
}
}

View File

@ -16,10 +16,11 @@
/// @brief MyThes-compatible thesaurus implementation
/// @ingroup libaegisub thesaurus
#include <libaegisub/scoped_ptr.h>
#include "fs_fwd.h"
#include <iosfwd>
#include <map>
#include <memory>
#include <string>
#include <vector>
@ -31,9 +32,9 @@ class Thesaurus {
/// Map of word -> byte position in the data file
std::map<std::string, int> offsets;
/// Read handle to the data file
scoped_ptr<std::ifstream> dat;
std::unique_ptr<std::istream> dat;
/// Converter from the data file's charset to UTF-8
scoped_ptr<charset::IconvWrapper> conv;
std::unique_ptr<charset::IconvWrapper> conv;
public:
/// A pair of a word and synonyms for that word
@ -42,7 +43,7 @@ public:
/// Constructor
/// @param dat_path Path to data file
/// @param idx_path Path to index file
Thesaurus(std::string const& dat_path, std::string const& idx_path);
Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path);
~Thesaurus();
/// Look up synonyms for a word

View File

@ -16,57 +16,31 @@
/// @brief Public interface for general utilities.
/// @ingroup libaegisub
#include <cstdint>
#include <string>
#include <algorithm>
#include <cstdint>
#include <string>
#include <libaegisub/types.h>
struct tm;
namespace agi {
namespace util {
/// Whether the path is a file or directory.
enum PathType {
TypeFile, ///< File
TypeDir ///< Directory
};
/// Clamp `b` to the range [`a`,`c`]
template<typename T> inline T mid(T a, T b, T c) { return std::max(a, std::min(b, c)); }
/// Get the parent directory of a path.
/// @param path Path to process.
const std::string DirName(const std::string& path);
/// Rename a file.
/// @param from Source.
/// @param to Destination.
void Rename(const std::string& from, const std::string& to);
/// Delete a file
/// @param path Path to file to delete
/// @throws agi::FileNotAccessibleError if file exists but could not be deleted
void Remove(std::string const& path);
/// Get time suitable for logging mechanisms.
/// @param tv timeval
void time_log(agi_timeval &tv);
/// Make all alphabetic characters lowercase.
/// @param str Input string
void str_lower(std::string &str);
/// Convert a string to Integer.
/// @param str Input string
int strtoi(std::string const& str);
bool try_parse(std::string const& str, double *out);
bool try_parse(std::string const& str, int *out);
/// Check for amount of free space on a Path.
/// @param path[in] Path to check
/// @param type PathType (default is TypeDir)
uint64_t freespace(std::string const& path, PathType type=TypeDir);
/// strftime, but on std::string rather than a fixed buffer
/// @param fmt strftime format string
/// @param tmptr Time to format, or nullptr for current time
/// @return The strftime-formatted string
std::string strftime(const char *fmt, const tm *tmptr = nullptr);
struct delete_ptr {
template<class T>

View File

@ -28,6 +28,8 @@
/// When linking with this library, be sure to add '-framework CoreFoundation'
/// to the GCC commandline.
#ifdef __APPLE__
#include <string>
namespace agi {
@ -91,3 +93,5 @@ std::string OSX_GetBundleAuxillaryExecutablePath(std::string const& executableNa
void OSX_OpenLocation(std::string const& location);
} // namespace io
} // namespace agi
#endif

View File

@ -23,9 +23,6 @@
namespace agi {
namespace util {
std::string ErrorString(DWORD error);
std::string ErrorString(DWORD error);
} // namespace util
} // namespace agi

View File

@ -24,6 +24,7 @@
#include <cstdint>
#include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
namespace agi {
/// Framerate handling.
@ -92,7 +93,7 @@ public:
/// not the same thing as CFR X. When timecodes are loaded from a file,
/// mkvmerge-style rounding is applied, while setting a constant frame rate
/// uses truncation.
Framerate(std::string const& filename);
Framerate(fs::path const& filename);
/// @brief CFR constructor
/// @param fps Frames per second or 0 for unloaded
@ -195,7 +196,7 @@ public:
/// CFR, but saving CFR timecodes is a bit silly). Extra timecodes generated
/// to hit length with v2 timecodes will monotonically increase but may not
/// be otherwise sensible.
void Save(std::string const& file, int length = -1) const;
void Save(fs::path const& file, int length = -1) const;
/// Is this frame rate possibly variable?
bool IsVFR() const {return timecodes.size() > 1; }

View File

@ -1,5 +1,7 @@
#include "config.h"
#define WIN32_LEAN_AND_MEAN
// Common C
#include <cassert>
#include <cerrno>

View File

@ -20,59 +20,30 @@
#include "libaegisub/access.h"
#include "libaegisub/fs.h"
#include <sys/stat.h>
#include <errno.h>
#include <iostream>
#include <fstream>
#include <unistd.h>
#include "libaegisub/util.h"
#include <boost/filesystem/path.hpp>
namespace agi {
namespace acs {
void CheckFileRead(const std::string &file) {
Check(file, acs::FileRead);
}
void CheckFileWrite(const std::string &file) {
Check(file, acs::FileWrite);
}
void CheckDirRead(const std::string &dir) {
Check(dir, acs::DirRead);
}
void CheckDirWrite(const std::string &dir) {
Check(dir, acs::DirWrite);
}
void Check(const std::string &file, acs::Type type) {
void Check(agi::fs::path const& file, acs::Type type) {
struct stat file_stat;
int file_status;
file_status = stat(file.c_str(), &file_stat);
int file_status = stat(file.c_str(), &file_stat);
if (file_status != 0) {
switch (errno) {
case ENOENT:
throw FileNotFoundError("File or path not found.");
break;
throw fs::FileNotFound(file);
case EACCES:
throw Read("Access Denied to file, path or path component.");
break;
throw fs::ReadDenied(file);
case EIO:
throw Fatal("Fatal I/O error occurred.");
break;
throw fs::FileSystemUnknownError("Fatal I/O error in 'stat' on path: " + file.string());
}
}
@ -80,24 +51,24 @@ void Check(const std::string &file, acs::Type type) {
case FileRead:
case FileWrite:
if ((file_stat.st_mode & S_IFREG) == 0)
throw NotAFile("Not a file.");
throw fs::NotAFile(file);
break;
case DirRead:
case DirWrite:
if ((file_stat.st_mode & S_IFDIR) == 0)
throw NotADirectory("Not a directory.");
throw fs::NotADirectory(file);
break;
}
file_status = access(file.c_str(), R_OK);
if (file_status != 0)
throw Read("File or directory is not readable.");
throw fs::ReadDenied(file);
if (type == DirWrite || type == FileWrite) {
file_status = access(file.c_str(), W_OK);
if (file_status != 0)
throw Write("File or directory is not writable.");
throw fs::WriteDenied(file);
}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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/fs.h"
#include <boost/filesystem.hpp>
#include <fcntl.h>
#include <fnmatch.h>
#include <sys/time.h>
namespace bfs = boost::filesystem;
namespace agi { namespace fs {
std::string ShortName(path const& p) {
return p.string();
}
void Touch(path const& file) {
CreateDirectory(file.parent_path());
int fd = open(file.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0644);
if (fd >= 0) {
futimes(fd, nullptr);
close(fd);
}
}
struct DirectoryIterator::PrivData {
boost::system::error_code ec;
bfs::directory_iterator it;
std::string filter;
PrivData(path const& p, std::string const& filter) : it(p, ec), filter(filter) { }
bool bad() const {
return
it == bfs::directory_iterator() ||
(!filter.empty() && fnmatch(filter.c_str(), it->path().filename().c_str(), 0));
}
};
DirectoryIterator::DirectoryIterator() { }
DirectoryIterator::DirectoryIterator(path const& p, std::string const& filter)
: privdata(new PrivData(p, filter))
{
if (privdata->it == bfs::directory_iterator())
privdata.reset();
else if (privdata->bad())
++*this;
else
value = privdata->it->path().filename().string();
}
bool DirectoryIterator::operator==(DirectoryIterator const& rhs) const {
return privdata.get() == rhs.privdata.get();
}
DirectoryIterator& DirectoryIterator::operator++() {
if (!privdata) return *this;
++privdata->it;
while (privdata->bad()) {
if (privdata->it == bfs::directory_iterator()) {
privdata.reset();
return *this;
}
++privdata->it;
}
value = privdata->it->path().filename().string();
return *this;
}
DirectoryIterator::~DirectoryIterator() { }
} }

View File

@ -44,8 +44,8 @@ void EmitSTDOUT::log(SinkMessage *sm) {
sm->file,
sm->func,
sm->line,
(int)sm->len,
sm->message);
(int)sm->message.size(),
sm->message.c_str());
if (!isatty(fileno(stdout)))
fflush(stdout);
}

View File

@ -0,0 +1,59 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <libaegisub/path.h>
#include <libaegisub/util_osx.h>
#include <boost/filesystem.hpp>
#include <pwd.h>
namespace {
std::string home_dir() {
const char *env = getenv("HOME");
if (env) return env;
if ((env = getenv("USER")) || (env = getenv("LOGNAME"))) {
if (passwd *user_info = getpwnam(env))
return user_info->pw_dir;
}
throw agi::EnvironmentError("Could not get home directory. Make sure HOME is set.");
}
std::string data_dir() {
#ifndef __APPLE__
return P_DATA;
#else
return agi::util::OSX_GetBundleSharedSupportDirectory();
#endif
}
}
namespace agi {
void Path::FillPlatformSpecificPaths() {
agi::fs::path home = home_dir();
SetToken("?user", home/".aegisub");
SetToken("?local", home/".aegisub");
SetToken("?data", data_dir());
SetToken("?temp", boost::filesystem::temp_directory_path());
SetToken("?dictionary", "/usr/share/hunspell");
SetToken("?docs", P_DOC);
}
}

View File

@ -20,72 +20,10 @@
#include "libaegisub/util.h"
#include "libaegisub/access.h"
#include <stdarg.h>
#include <stdio.h>
#include <sys/statvfs.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#include <string>
#include <fstream>
#include <string.h>
namespace agi {
namespace util {
const std::string DirName(const std::string& path) {
if (path.find('/') == std::string::npos) {
return ".";
}
return path.substr(0, path.rfind("/")+1);
}
void Rename(const std::string& from, const std::string& to) {
acs::CheckFileWrite(from);
try {
acs::CheckFileWrite(to);
} catch (FileNotFoundError const&) {
acs::CheckDirWrite(DirName(to));
}
rename(from.c_str(), to.c_str());
}
void Remove(std::string const& path) {
if (!remove(path.c_str()) && errno != ENOENT)
throw agi::FileNotAccessibleError("Can not remove file: " + path);
}
namespace agi { namespace util {
void time_log(timeval &tv) {
gettimeofday(&tv, (struct timezone *)NULL);
}
uint64_t freespace(std::string const& path, PathType type) {
struct statvfs fs;
std::string check(path);
if (type == TypeFile)
check.assign(DirName(path));
acs::CheckDirRead(check);
if ((statvfs(check.c_str(), &fs)) == 0) {
return (uint64_t)fs.f_bsize * fs.f_bavail;
} else {
/// @todo We need a collective set of exceptions for ENOTDIR, EIO etc.
throw("Failed getting free space");
}
}
} // namespace io
} // namespace agi
} }

View File

@ -16,18 +16,19 @@
/// @brief Windows access methods.
/// @ingroup libaegisub windows
#include <windows.h>
#include <iostream>
#include <fstream>
#include <libaegisub/access.h>
#include <libaegisub/charset_conv_win.h>
#include <libaegisub/fs.h>
#include <libaegisub/log.h>
#include <libaegisub/util.h>
#include <libaegisub/util_win.h>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <windows.h>
namespace {
bool check_permission(bool is_read, SECURITY_DESCRIPTOR *sd, HANDLE client_token) {
DWORD access_check = is_read ? FILE_READ_DATA : FILE_APPEND_DATA | FILE_WRITE_DATA;
@ -48,43 +49,23 @@ namespace {
namespace agi {
namespace acs {
void CheckFileRead(const std::string &file) {
Check(file, acs::FileRead);
}
void CheckFileWrite(const std::string &file) {
Check(file, acs::FileWrite);
}
void CheckDirRead(const std::string &dir) {
Check(dir, acs::DirRead);
}
void CheckDirWrite(const std::string &dir) {
Check(dir, acs::DirWrite);
}
/*
This function is still a proof of concept, it's probably rife with bugs, below
is a short (and incomplete) todo
* "Basic" checks (Read/Write/File/Dir) checks for FAT32 filesystems which
requires detecting the filesystem being used.
*/
void Check(const std::string &file, acs::Type type) {
std::wstring wfile = agi::charset::ConvertW(file);
DWORD file_attr = GetFileAttributes(wfile.c_str());
void Check(fs::path const& file, acs::Type type) {
DWORD file_attr = GetFileAttributes(file.c_str());
if ((file_attr & INVALID_FILE_ATTRIBUTES) == INVALID_FILE_ATTRIBUTES) {
switch (GetLastError()) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
throw FileNotFoundError(file);
throw fs::FileNotFound(file);
case ERROR_ACCESS_DENIED:
throw Read("Access denied to file or path component");
throw fs::ReadDenied(file);
default:
throw Fatal("Fatal I/O error occurred.");
throw fs::FileSystemUnknownError(str(boost::format("Unexpected error when getting attributes for \"%s\": %s") % file % util::ErrorString(GetLastError())));
}
}
@ -92,25 +73,25 @@ void Check(const std::string &file, acs::Type type) {
case FileRead:
case FileWrite:
if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
throw NotAFile(file + " is not a file");
throw fs::NotAFile(file);
break;
case DirRead:
case DirWrite:
if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
throw NotADirectory(file + " is not a directory");
throw fs::NotADirectory(file);
break;
}
SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
DWORD len = 0;
GetFileSecurity(wfile.c_str(), info, nullptr, 0, &len);
GetFileSecurity(file.c_str(), info, nullptr, 0, &len);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
LOG_W("acs/check") << "GetFileSecurity: fatal: " << util::ErrorString(GetLastError());
std::vector<uint8_t> sd_buff(len);
SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)&sd_buff[0];
if (!GetFileSecurity(wfile.c_str(), info, sd, len, &len))
if (!GetFileSecurity(file.c_str(), info, sd, len, &len))
LOG_W("acs/check") << "GetFileSecurity failed: " << util::ErrorString(GetLastError());
ImpersonateSelf(SecurityImpersonation);
@ -119,9 +100,9 @@ void Check(const std::string &file, acs::Type type) {
LOG_W("acs/check") << "OpenThreadToken failed: " << util::ErrorString(GetLastError());
if (!check_permission(true, sd, client_token))
throw Read("File or directory is not readable");
throw fs::ReadDenied(file);
if ((type == DirWrite || type == FileWrite) && !check_permission(false, sd, client_token))
throw Write("File or directory is not writable");
throw fs::WriteDenied(file);
}
} // namespace Access

View File

@ -18,6 +18,18 @@
#include <libaegisub/charset_conv_win.h>
namespace {
std::string from_w(agi::charset::IconvWrapper &w32Conv, std::wstring const& source) {
std::string dest;
size_t srcLen = source.size() * sizeof(wchar_t);
const char* src = reinterpret_cast<const char *>(source.c_str());
size_t len = w32Conv.RequiredBufferSize(src, srcLen);
dest.resize(len);
w32Conv.Convert(src, srcLen, &dest[0], len);
return dest;
}
}
namespace agi {
namespace charset {
@ -33,14 +45,12 @@ std::wstring ConvertW(std::string const& source) {
std::string ConvertW(std::wstring const& source) {
static IconvWrapper w32Conv("utf-16le", "utf-8", false);
return from_w(w32Conv, source);
}
std::string dest;
size_t srcLen = source.size() * sizeof(wchar_t);
const char* src = reinterpret_cast<const char *>(source.c_str());
size_t len = w32Conv.RequiredBufferSize(src, srcLen);
dest.resize(len);
w32Conv.Convert(src, srcLen, &dest[0], len);
return dest;
std::string ConvertLocal(std::wstring const& source) {
static IconvWrapper w32Conv("utf-16le", "char", false);
return from_w(w32Conv, source);
}
}

View File

@ -0,0 +1,99 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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/fs.h"
#include "libaegisub/access.h"
#include "libaegisub/charset_conv_win.h"
#include "libaegisub/exception.h"
#include "libaegisub/scoped_ptr.h"
#include "libaegisub/util_win.h"
using agi::charset::ConvertW;
using agi::charset::ConvertLocal;
#include <boost/filesystem.hpp>
namespace bfs = boost::filesystem;
#undef CreateDirectory
namespace agi { namespace fs {
std::string ShortName(path const& p) {
std::wstring out(MAX_PATH + 1, 0);
DWORD len = GetShortPathName(p.c_str(), &out[0], out.size());
if (!len)
return p.string();
out.resize(len);
return ConvertLocal(out);
}
void Touch(path const& file) {
CreateDirectory(file.parent_path());
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
if(!SystemTimeToFileTime(&st, &ft))
throw EnvironmentError("SystemTimeToFileTime failed with error: " + util::ErrorString(GetLastError()));
scoped_holder<HANDLE, BOOL (__stdcall *)(HANDLE)>
h(CreateFile(file.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr), CloseHandle);
// error handling etc.
if (!SetFileTime(h, nullptr, nullptr, &ft))
throw EnvironmentError("SetFileTime failed with error: " + util::ErrorString(GetLastError()));
}
struct DirectoryIterator::PrivData {
scoped_holder<HANDLE, BOOL (__stdcall *)(HANDLE)> h;
PrivData() : h(INVALID_HANDLE_VALUE, FindClose) { }
};
DirectoryIterator::DirectoryIterator() { }
DirectoryIterator::DirectoryIterator(path const& p, std::string const& filter)
: privdata(new PrivData)
{
WIN32_FIND_DATA data;
privdata->h = FindFirstFileEx((p/(filter.empty() ? "*.*" : filter)).c_str(), FindExInfoBasic, &data, FindExSearchNameMatch, nullptr, 0);
if (privdata->h == INVALID_HANDLE_VALUE) {
privdata.reset();
return;
}
value = ConvertW(data.cFileName);
while (value[0] == '.' && (value[1] == 0 || value[1] == '.'))
++*this;
}
bool DirectoryIterator::operator==(DirectoryIterator const& rhs) const {
return privdata.get() == rhs.privdata.get();
}
DirectoryIterator& DirectoryIterator::operator++() {
WIN32_FIND_DATA data;
if (FindNextFile(privdata->h, &data))
value = ConvertW(data.cFileName);
else {
privdata.reset();
value.clear();
}
return *this;
}
DirectoryIterator::~DirectoryIterator() { }
} }

View File

@ -0,0 +1,57 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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/
/// @file path.cpp
/// @brief Windows-specific path code
/// @ingroup libaegisub windows
#include <libaegisub/path.h>
#include <libaegisub/util_win.h>
#include <boost/filesystem.hpp>
namespace {
#include <Shlobj.h>
#include <Shellapi.h>
agi::fs::path WinGetFolderPath(int folder) {
wchar_t path[MAX_PATH+1] = {0};
if (FAILED(SHGetFolderPathW(0, folder, 0, 0, path)))
throw agi::EnvironmentError("SHGetFolderPath failed. This should not happen.");
return path;
}
}
namespace agi {
void Path::FillPlatformSpecificPaths() {
tokens["?temp"] = boost::filesystem::temp_directory_path();
SetToken("?user", WinGetFolderPath(CSIDL_APPDATA)/"Aegisub");
SetToken("?local", WinGetFolderPath(CSIDL_LOCAL_APPDATA)/"Aegisub");
/// @todo error checking
int argc;
LPWSTR *argv = CommandLineToArgvW(L"", &argc);
SetToken("?data", argv[0]);
LocalFree(argv);
SetToken("?dictionary", Decode("?data/dictionaries"));
//SetToken("?docs", Decode("?data/docs"));
}
}

View File

@ -16,55 +16,18 @@
/// @brief Windows utility methods.
/// @ingroup libaegisub windows
#include <stdarg.h>
#include <stdio.h>
#include "libaegisub/util_win.h"
#include <string>
#include <fstream>
#include <windows.h>
#include "libaegisub/access.h"
#include "libaegisub/charset_conv_win.h"
#include "libaegisub/types.h"
#include "libaegisub/util.h"
#include "libaegisub/util_win.h"
namespace agi {
namespace util {
using agi::charset::ConvertW;
const std::string DirName(const std::string& path) {
std::string::size_type pos = path.rfind('/');
if (pos == std::string::npos) pos = path.rfind('\\');
if (pos == std::string::npos) return ".";
return path.substr(0, pos+1);
}
void Rename(const std::string& from, const std::string& to) {
acs::CheckFileWrite(from);
try {
acs::CheckFileWrite(to);
} catch (FileNotFoundError const&) {
acs::CheckDirWrite(DirName(to));
}
if (!MoveFileEx(ConvertW(from).c_str(), ConvertW(to).c_str(), MOVEFILE_REPLACE_EXISTING))
throw agi::FileNotAccessibleError("Can not overwrite file: " + ErrorString(GetLastError()));
}
void Remove(std::string const& path) {
if (!DeleteFile(ConvertW(path).c_str())) {
DWORD err = GetLastError();
if (err != ERROR_FILE_NOT_FOUND)
throw agi::FileNotAccessibleError("Can not remove file: " + ErrorString(err));
}
}
std::string ErrorString(DWORD error) {
LPWSTR lpstr = nullptr;
@ -116,19 +79,5 @@ void time_log(agi_timeval &tv) {
tv.tv_usec = (long)(tmpres % 1000000UL);
}
uint64_t freespace(std::string const& path, PathType type) {
if (type == TypeFile)
return freespace(DirName(path));
ULARGE_INTEGER bytes_available;
if (GetDiskFreeSpaceEx(ConvertW(path).c_str(), &bytes_available, 0, 0))
return bytes_available.QuadPart;
acs::CheckDirRead(path);
/// @todo GetLastError -> Exception mapping
throw "Unknown error getting free space";
}
} // namespace io
} // namespace agi

View File

@ -157,7 +157,6 @@ SRC += \
auto4_base.cpp \
avisynth_wrap.cpp \
base_grid.cpp \
charset_conv.cpp \
charset_detect.cpp \
colorspace.cpp \
colour_button.cpp \

View File

@ -36,18 +36,16 @@
#include "aegisublocale.h"
#include "standard_paths.h"
#include <algorithm>
#include <boost/filesystem/path.hpp>
#include <clocale>
#include <functional>
#include <wx/dir.h>
#include <wx/filename.h>
#include <wx/intl.h>
#include <wx/stdpaths.h>
#include <wx/choicdlg.h> // Keep this last so wxUSE_CHOICEDLG is set.
#include "standard_paths.h"
#ifndef AEGISUB_CATALOG
#define AEGISUB_CATALOG "aegisub"
#endif
@ -56,7 +54,7 @@ wxTranslations *AegisubLocale::GetTranslations() {
wxTranslations *translations = wxTranslations::Get();
if (!translations) {
wxTranslations::Set(translations = new wxTranslations);
wxFileTranslationsLoader::AddCatalogLookupPathPrefix(StandardPaths::DecodePath("?data/locale/"));
wxFileTranslationsLoader::AddCatalogLookupPathPrefix(StandardPaths::DecodePath("?data/locale/").wstring());
}
return translations;
}

View File

@ -74,7 +74,6 @@
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/indirected.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/algorithm_ext.hpp>
// wxWidgets headers
#include <wx/wxprec.h> // Leave this first.

View File

@ -34,26 +34,33 @@
#include "config.h"
#include <wx/filename.h>
#include <istream>
#include "ass_attachment.h"
#include "compat.h"
#include <libaegisub/io.h>
#include <libaegisub/scoped_ptr.h>
AssAttachment::AssAttachment(wxString const& name, AssEntryGroup group)
#include <boost/algorithm/string/predicate.hpp>
#include <fstream>
AssAttachment::AssAttachment(std::string const& name, AssEntryGroup group)
: data(new std::vector<char>)
, filename(name)
, group(group)
{
wxFileName fname(filename);
wxString ext = fname.GetExt().Lower();
if (ext == "ttf")
filename = fname.GetName() + "_0." + ext;
}
AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group)
: data(new std::vector<char>)
, filename(name.filename().string())
, group(group)
{
if (boost::iends_with(filename, ".ttf"))
filename = filename.substr(0, filename.size() - 4) + "_0" + filename.substr(filename.size() - 4);
std::unique_ptr<std::istream> file(agi::io::Open(name, true));
file->seekg(0, std::ios::end);
data->resize(file->tellg());
file->seekg(0, std::ios::beg);
file->read(&(*data)[0], data->size());
}
AssEntry *AssAttachment::Clone() const {
@ -62,46 +69,27 @@ AssEntry *AssAttachment::Clone() const {
return clone;
}
const wxString AssAttachment::GetEntryData() const {
size_t pos = 0;
const std::string AssAttachment::GetEntryData() const {
size_t size = data->size();
size_t written = 0;
unsigned char src[3];
unsigned char dst[4];
// Write header
wxString entryData = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename + "\r\n";
std::string entryData = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename + "\r\n";
entryData.reserve(size * 4 / 3 + size / 80 * 2 + entryData.size() + 2);
// Read three bytes
while (pos < size) {
// Number to read
size_t read = size - pos;
if (read > 3) read = 3;
for (size_t pos = 0; pos < size; pos += 3) {
unsigned char src[3] = { '\0', '\0', '\0' };
memcpy(src, &(*data)[pos], std::min<size_t>(3u, size - pos));
// Read source
src[0] = (*data)[pos];
if (read >= 2) src[1] = (*data)[pos+1];
else src[1] = 0;
if (read == 3) src[2] = (*data)[pos+2];
else src[2] = 0;
pos += read;
// Codify
unsigned char dst[4];
dst[0] = src[0] >> 2;
dst[1] = ((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4);
dst[2] = ((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6);
dst[3] = src[2] & 0x3F;
// Number to write
size_t toWrite = read+1;
for (size_t i = 0; i < std::min<size_t>(size - pos + 1, 4u); ++i) {
entryData += dst[i] + 33;
// Convert to text
for (size_t i=0;i<toWrite;i++) {
entryData += dst[i]+33;
written++;
// Line break
if (written == 80 && pos < size) {
if (++written == 80 && pos + 3 < size) {
written = 0;
entryData += "\r\n";
}
@ -111,39 +99,28 @@ const wxString AssAttachment::GetEntryData() const {
return entryData;
}
void AssAttachment::Extract(wxString const& filename) const {
agi::io::Save(from_wx(filename), true).Get().write(&(*data)[0], data->size());
void AssAttachment::Extract(agi::fs::path const& filename) const {
agi::io::Save(filename, true).Get().write(&(*data)[0], data->size());
}
void AssAttachment::Import(wxString const& filename) {
agi::scoped_ptr<std::istream> file(agi::io::Open(from_wx(filename), true));
file->seekg(0, std::ios::end);
data->resize(file->tellg());
file->seekg(0, std::ios::beg);
file->read(&(*data)[0], data->size());
}
wxString AssAttachment::GetFileName(bool raw) const {
if (raw || filename.Right(4).Lower() != ".ttf") return filename;
std::string AssAttachment::GetFileName(bool raw) const {
if (raw || !boost::iends_with(filename, ".ttf")) return filename;
// Remove stuff after last underscore if it's a font
wxString::size_type last_under = filename.rfind('_');
if (last_under == wxString::npos)
std::string::size_type last_under = filename.rfind('_');
if (last_under == std::string::npos)
return filename;
return filename.Left(last_under) + ".ttf";
return filename.substr(0, last_under) + ".ttf";
}
void AssAttachment::Finish() {
// Source and dest buffers
unsigned char src[4];
unsigned char dst[3];
data->reserve(buffer.size() * 3 / 4);
// Read buffer
for(size_t pos = 0; pos + 1 < buffer.size(); ) {
// Find characters left
size_t read = std::min<size_t>(buffer.size() - pos, 4);
// Move 4 bytes from buffer to src
@ -157,11 +134,9 @@ void AssAttachment::Finish() {
dst[1] = ((src[1] & 0xF) << 4) | (src[2] >> 2);
dst[2] = ((src[2] & 0x3) << 6) | (src[3]);
// Push into vector
copy(dst, dst + read - 1, back_inserter(*data));
}
// Clear buffer
buffer.clear();
buffer.Shrink();
buffer.shrink_to_fit();
}

View File

@ -32,21 +32,23 @@
/// @ingroup subs_storage
///
#include "ass_entry.h"
#include <libaegisub/fs_fwd.h>
#include <memory>
#include <vector>
#include "ass_entry.h"
/// @class AssAttachment
class AssAttachment : public AssEntry {
/// Decoded file data
std::shared_ptr<std::vector<char>> data;
/// Encoded data which has been read from the script but not yet decoded
wxString buffer;
std::vector<char> buffer;
/// Name of the attached file, with SSA font mangling if it is a ttf
wxString filename;
std::string filename;
AssEntryGroup group;
@ -56,25 +58,22 @@ public:
/// Add a line of data (without newline) read from a subtitle file to the
/// buffer waiting to be decoded
void AddData(wxString const& data) { buffer += data; }
void AddData(std::string const& data) { buffer.insert(buffer.end(), data.begin(), data.end()); }
/// Decode all data passed with AddData
void Finish();
/// Extract the contents of this attachment to a file
/// @param filename Path to save the attachment to
void Extract(wxString const& filename) const;
/// Import the contents of a file as an attachment
/// @param filename Path to import
void Import(wxString const& filename);
void Extract(agi::fs::path const& filename) const;
/// Get the name of the attached file
/// @param raw If false, remove the SSA filename mangling
wxString GetFileName(bool raw=false) const;
std::string GetFileName(bool raw=false) const;
const wxString GetEntryData() const override;
const std::string GetEntryData() const override;
AssEntryGroup Group() const override { return group; }
AssEntry *Clone() const override;
AssAttachment(wxString const& name, AssEntryGroup group);
AssAttachment(std::string const& name, AssEntryGroup group);
AssAttachment(agi::fs::path const& name, AssEntryGroup group);
};

View File

@ -34,27 +34,24 @@
#include "config.h"
#include "ass_dialogue.h"
#include "compat.h"
#include "subtitle_format.h"
#include "utils.h"
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/split.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
#include <wx/regex.h>
#include <wx/tokenzr.h>
#include <boost/spirit/include/karma_generate.hpp>
#include <boost/spirit/include/karma_int.hpp>
using namespace boost::adaptors;
static int next_id = 0;
std::size_t hash_value(wxString const& s) {
return wxStringHash()(s);
}
AssDialogue::AssDialogue()
: Id(++next_id)
, Comment(false)
@ -80,105 +77,91 @@ AssDialogue::AssDialogue(AssDialogue const& that)
memmove(Margin, that.Margin, sizeof Margin);
}
AssDialogue::AssDialogue(wxString const& data)
AssDialogue::AssDialogue(std::string const& data)
: Id(++next_id)
{
if (!Parse(data))
throw SubtitleFormatParseError(from_wx("Failed parsing line: " + data), 0);
Parse(data);
}
AssDialogue::~AssDialogue () {
}
bool AssDialogue::Parse(wxString const& rawData) {
size_t pos = 0;
wxString temp;
class tokenizer {
agi::StringRange str;
boost::split_iterator<agi::StringRange::const_iterator> pos;
// Get type
if (rawData.StartsWith("Dialogue:")) {
public:
tokenizer(agi::StringRange const& str) : str(str) , pos(agi::Split(str, ',')) { }
agi::StringRange next_tok() {
if (pos.eof())
throw SubtitleFormatParseError("Failed parsing line: " + std::string(str.begin(), str.end()), 0);
return *pos++;
}
std::string next_str() { return agi::str(next_tok()); }
std::string next_str_trim() { return agi::str(boost::trim_copy(next_tok())); }
};
void AssDialogue::Parse(std::string const& raw) {
agi::StringRange str;
if (boost::starts_with(raw, "Dialogue:")) {
Comment = false;
pos = 10;
str = agi::StringRange(raw.begin() + 10, raw.end());
}
else if (rawData.StartsWith("Comment:")) {
else if (boost::starts_with(raw, "Comment:")) {
Comment = true;
pos = 9;
str = agi::StringRange(raw.begin() + 9, raw.end());
}
else return false;
else
throw SubtitleFormatParseError("Failed parsing line: " + raw, 0);
wxStringTokenizer tkn(rawData.Mid(pos),",",wxTOKEN_RET_EMPTY_ALL);
if (!tkn.HasMoreTokens()) return false;
tokenizer tkn(str);
// Get first token and see if it has "Marked=" in it
temp = tkn.GetNextToken().Trim(false).Trim(true);
bool ssa = temp.Lower().StartsWith("marked=");
auto tmp = tkn.next_str_trim();
bool ssa = boost::istarts_with(tmp, "marked=");
// Get layer number
if (ssa)
Layer = 0;
else {
long templ;
temp.ToLong(&templ);
Layer = templ;
}
else
Layer = boost::lexical_cast<int>(tmp);
// Get start time
if (!tkn.HasMoreTokens()) return false;
Start = tkn.GetNextToken();
// Get end time
if (!tkn.HasMoreTokens()) return false;
End = tkn.GetNextToken();
// Get style
if (!tkn.HasMoreTokens()) return false;
Style = tkn.GetNextToken().Trim(true).Trim(false);
// Get actor
if (!tkn.HasMoreTokens()) return false;
Actor = tkn.GetNextToken().Trim(true).Trim(false);
// Get margins
for (int i = 0; i < 3; ++i) {
if (!tkn.HasMoreTokens()) return false;
SetMarginString(tkn.GetNextToken().Trim(false).Trim(true), i);
}
if (!tkn.HasMoreTokens()) return false;
Effect = tkn.GetNextToken().Trim(true).Trim(false);
// Get text
Text = rawData.Mid(pos + tkn.GetPosition());
return true;
Start = tkn.next_str_trim();
End = tkn.next_str_trim();
Style = tkn.next_str_trim();
Actor = tkn.next_str_trim();
for (int& margin : Margin)
margin = mid(0, boost::lexical_cast<int>(tkn.next_str()), 9999);
Effect = tkn.next_str_trim();
Text = std::string(tkn.next_tok().begin(), str.end());
}
static void append_int(wxString &str, int v) {
str += std::to_wstring(v);
void append_int(std::string &str, int v) {
boost::spirit::karma::generate(back_inserter(str), boost::spirit::karma::int_, v);
str += ',';
}
static void append_str(wxString &out, wxString const& str) {
void append_str(std::string &out, std::string const& str) {
out += str;
out += ',';
}
static void append_unsafe_str(wxString &out, wxString const& str) {
void append_unsafe_str(std::string &out, std::string const& str) {
if (str.find(',') == str.npos)
out += str;
else {
wxString c = str;
c.Replace(wxS(","), wxS(";"));
out += c;
}
else
out += boost::replace_all_copy(str, ",", ";");
out += ',';
}
wxString AssDialogue::GetData(bool ssa) const {
wxString str = Comment ? wxS("Comment: ") : wxS("Dialogue: ");
std::string AssDialogue::GetData(bool ssa) const {
std::string str = Comment ? "Comment: " : "Dialogue: ";
str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size());
if (ssa)
append_str(str, wxS("Marked=0"));
append_str(str, "Marked=0");
else
append_int(str, Layer);
append_str(str, Start.GetAssFormated());
@ -190,20 +173,19 @@ wxString AssDialogue::GetData(bool ssa) const {
append_unsafe_str(str, Effect);
str += Text.get();
// Make sure that final has no line breaks
if (str.find('\n') != str.npos || str.find('\r') != str.npos) {
str.Replace("\n", "");
str.Replace("\r", "");
boost::replace_all(str, "\n", "");
boost::replace_all(str, "\r", "");
}
return str;
}
const wxString AssDialogue::GetEntryData() const {
const std::string AssDialogue::GetEntryData() const {
return GetData(false);
}
wxString AssDialogue::GetSSAText() const {
std::string AssDialogue::GetSSAText() const {
return GetData(true);
}
@ -217,7 +199,7 @@ std::auto_ptr<boost::ptr_vector<AssDialogueBlock>> AssDialogue::ParseTags() cons
}
int drawingLevel = 0;
std::string text(from_wx(Text.get()));
std::string const& text(Text.get());
for (size_t len = text.size(), cur = 0; cur < len; ) {
// Overrides block
@ -284,32 +266,17 @@ void AssDialogue::StripTags() {
static std::string get_text(AssDialogueBlock &d) { return d.GetText(); }
void AssDialogue::UpdateText(boost::ptr_vector<AssDialogueBlock>& blocks) {
if (blocks.empty()) return;
Text = to_wx(join(blocks | transformed(get_text), ""));
Text = join(blocks | transformed(get_text), "");
}
void AssDialogue::SetMarginString(wxString const& origvalue, int which) {
void AssDialogue::SetMarginString(std::string const& origvalue, int which) {
if (which < 0 || which > 2) throw InvalidMarginIdError();
// Make it numeric
wxString strvalue = origvalue;
if (!strvalue.IsNumber()) {
strvalue.clear();
for (size_t i = 0; i < origvalue.Length(); ++i) {
if (origvalue.Mid(i, 1).IsNumber()) {
strvalue += origvalue.Mid(i, 1);
}
}
}
// Get value
long value = 0;
strvalue.ToLong(&value);
Margin[which] = mid<int>(0, value, 9999);
Margin[which] = mid<int>(0, atoi(origvalue.c_str()), 9999);
}
wxString AssDialogue::GetMarginString(int which) const {
std::string AssDialogue::GetMarginString(int which) const {
if (which < 0 || which > 2) throw InvalidMarginIdError();
return wxString::Format("%d", Margin[which]);
return std::to_string(Margin[which]);
}
bool AssDialogue::CollidesWith(const AssDialogue *target) const {
@ -318,10 +285,9 @@ bool AssDialogue::CollidesWith(const AssDialogue *target) const {
}
static std::string get_text_p(AssDialogueBlock *d) { return d->GetText(); }
wxString AssDialogue::GetStrippedText() const {
wxString ret;
std::string AssDialogue::GetStrippedText() const {
boost::ptr_vector<AssDialogueBlock> blocks(ParseTags());
return to_wx(join(blocks | agi::of_type<AssDialogueBlockPlain>() | transformed(get_text_p), ""));
return join(blocks | agi::of_type<AssDialogueBlockPlain>() | transformed(get_text_p), "");
}
AssEntry *AssDialogue::Clone() const {
@ -336,10 +302,9 @@ void AssDialogueBlockDrawing::TransformCoords(int mx, int my, double x, double y
bool is_x = true;
std::string final;
boost::char_separator<char> sep(" ");
for (auto const& cur : boost::tokenizer<boost::char_separator<char>>(text, sep)) {
for (auto const& cur : agi::Split(text, ' ')) {
if (std::all_of(begin(cur), end(cur), isdigit)) {
int val = boost::lexical_cast<int>(cur);
int val = boost::lexical_cast<int>(agi::str(cur));
if (is_x)
val = (int)((val + mx) * x + .5);
else

View File

@ -50,8 +50,6 @@ enum AssBlockType {
BLOCK_DRAWING
};
std::size_t hash_value(wxString const& s);
/// @class AssDialogueBlock
/// @brief AssDialogue Blocks
///
@ -126,7 +124,11 @@ public:
};
class AssDialogue : public AssEntry {
wxString GetData(bool ssa) const;
std::string GetData(bool ssa) const;
/// @brief Parse raw ASS data into everything else
/// @param data ASS line
void Parse(std::string const& data);
public:
/// Unique ID of this line. Copies of the line for Undo/Redo purposes
/// preserve the unique ID, so that the equivalent lines can be found in
@ -144,21 +146,16 @@ public:
/// Ending time
AssTime End;
/// Style name
boost::flyweight<wxString> Style;
boost::flyweight<std::string> Style;
/// Actor name
boost::flyweight<wxString> Actor;
boost::flyweight<std::string> Actor;
/// Effect name
boost::flyweight<wxString> Effect;
boost::flyweight<std::string> Effect;
/// Raw text data
boost::flyweight<wxString> Text;
boost::flyweight<std::string> Text;
AssEntryGroup Group() const override { return ENTRY_DIALOGUE; }
/// @brief Parse raw ASS data into everything else
/// @param data ASS line
/// @return Did it successfully parse?
bool Parse(wxString const& data);
/// Parse text as ASS and return block information
std::auto_ptr<boost::ptr_vector<AssDialogueBlock>> ParseTags() const;
@ -166,23 +163,23 @@ public:
void StripTags();
/// Strip a specific ASS tag from the text
/// Get text without tags
wxString GetStrippedText() const;
std::string GetStrippedText() const;
/// Update the text of the line from parsed blocks
void UpdateText(boost::ptr_vector<AssDialogueBlock>& blocks);
const wxString GetEntryData() const override;
const std::string GetEntryData() const override;
template<int which>
void SetMarginString(wxString const& value) { SetMarginString(value, which);}
void SetMarginString(std::string const& value) { SetMarginString(value, which);}
/// @brief Set a margin
/// @param value New value of the margin
/// @param which 0 = left, 1 = right, 2 = vertical
void SetMarginString(wxString const& value, int which);
void SetMarginString(std::string const& value, int which);
/// @brief Get a margin
/// @param which 0 = left, 1 = right, 2 = vertical
wxString GetMarginString(int which) const;
std::string GetMarginString(int which) const;
/// Get the line as SSA rather than ASS
wxString GetSSAText() const override;
std::string GetSSAText() const override;
/// Does this line collide with the passed line?
bool CollidesWith(const AssDialogue *target) const;
@ -190,7 +187,7 @@ public:
AssDialogue();
AssDialogue(AssDialogue const&);
AssDialogue(wxString const& data);
AssDialogue(std::string const& data);
~AssDialogue();
};

View File

@ -23,8 +23,8 @@
#include "ass_entry.h"
wxString const& AssEntry::GroupHeader(bool ssa) const {
static wxString ass_headers[] = {
std::string const& AssEntry::GroupHeader(bool ssa) const {
static std::string ass_headers[] = {
"[Script Info]",
"[V4+ Styles]",
"[Fonts]",
@ -33,7 +33,7 @@ wxString const& AssEntry::GroupHeader(bool ssa) const {
""
};
static wxString ssa_headers[] = {
static std::string ssa_headers[] = {
"[Script Info]",
"[V4 Styles]",
"[Fonts]",

View File

@ -34,9 +34,8 @@
#pragma once
#include <wx/string.h>
#include <boost/intrusive/list_hook.hpp>
#include <string>
enum AssEntryGroup {
ENTRY_INFO = 0,
@ -58,11 +57,11 @@ public:
virtual AssEntryGroup Group() const=0;
/// ASS or SSA Section header for this entry's group
wxString const& GroupHeader(bool ssa=false) const;
std::string const& GroupHeader(bool ssa=false) const;
/// @brief Get this line's raw entry data in ASS format
virtual const wxString GetEntryData() const=0;
virtual const std::string GetEntryData() const=0;
/// Get this line in SSA format
virtual wxString GetSSAText() const { return GetEntryData(); }
virtual std::string GetSSAText() const { return GetEntryData(); }
};

View File

@ -34,12 +34,19 @@
#include "config.h"
#include <algorithm>
#include "ass_export_filter.h"
#include "utils.h"
AssExportFilter::AssExportFilter(wxString const& name, wxString const& description, int priority)
#include <algorithm>
#include <boost/format.hpp>
static FilterList& filters() {
static FilterList instance;
return instance;
}
AssExportFilter::AssExportFilter(std::string const& name, std::string const& description, int priority)
: name(name)
, priority(priority)
, description(description)
@ -47,52 +54,44 @@ AssExportFilter::AssExportFilter(wxString const& name, wxString const& descripti
}
void AssExportFilterChain::Register(AssExportFilter *filter) {
// Remove pipes from name
filter->name.Replace("|", "");
int filter_copy = 1;
wxString name = filter->name;
std::string name = filter->name;
// Find a unique name
while (GetFilter(name))
name = wxString::Format("%s (%d)", filter->name, filter_copy++);
name = str(boost::format("%s (%d)") % filter->name % filter_copy++);
filter->name = name;
// Look for place to insert
FilterList::iterator begin = filters()->begin();
FilterList::iterator end = filters()->end();
auto begin(filters().begin()), end(filters().end());
while (begin != end && (*begin)->priority >= filter->priority) ++begin;
filters()->insert(begin, filter);
filters().insert(begin, filter);
}
void AssExportFilterChain::Unregister(AssExportFilter *filter) {
auto it = remove(begin(*filters()), end(*filters()), filter);
if (it == end(*filters()))
auto it = remove(begin(filters()), end(filters()), filter);
if (it == end(filters()))
throw wxString::Format("Unregister export filter: name \"%s\" is not registered.", filter->name);
filters()->pop_back();
filters().pop_back();
}
FilterList *AssExportFilterChain::filters() {
static FilterList instance;
return &instance;
}
const FilterList *AssExportFilterChain::GetFilterList() {
return filters();
return &filters();
}
void AssExportFilterChain::Clear() {
while (filters()->size() > 0) {
AssExportFilter *f = filters()->back();
while (filters().size() > 0) {
AssExportFilter *f = filters().back();
delete f;
if (filters()->size() && filters()->back() == f)
filters()->pop_back();
if (filters().size() && filters().back() == f)
filters().pop_back();
}
}
AssExportFilter *AssExportFilterChain::GetFilter(wxString const& name) {
for (auto filter : *filters()) {
AssExportFilter *AssExportFilterChain::GetFilter(std::string const& name) {
for (auto filter : filters()) {
if (filter->name == name)
return filter;
}

View File

@ -35,20 +35,18 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <wx/string.h>
#include <wx/window.h>
class AssFile;
class AssExportFilter;
class wxWindow;
namespace agi { struct Context; }
typedef std::vector<AssExportFilter*> FilterList;
class AssExportFilterChain {
static FilterList *filters();
public:
/// Register an export filter
static void Register(AssExportFilter *filter);
@ -57,7 +55,7 @@ public:
/// Unregister and delete all export filters
static void Clear();
/// Get a filter by name or nullptr if it doesn't exist
static AssExportFilter *GetFilter(wxString const& name);
static AssExportFilter *GetFilter(std::string const& name);
/// Get the list of registered filters
static const FilterList *GetFilterList();
@ -69,20 +67,20 @@ class AssExportFilter {
friend class AssExportFilterChain;
/// This filter's name
wxString name;
std::string name;
/// Higher priority = run earlier
int priority;
/// User-visible description of this filter
wxString description;
std::string description;
public:
AssExportFilter(wxString const& name, wxString const& description, int priority = 0);
AssExportFilter(std::string const& name, std::string const& description, int priority = 0);
virtual ~AssExportFilter() { };
wxString const& GetName() const { return name; }
wxString const& GetDescription() const { return description; }
std::string const& GetName() const { return name; }
std::string const& GetDescription() const { return description; }
/// Process subtitles
/// @param subs Subtitles to process

View File

@ -34,14 +34,16 @@
#include "config.h"
#include "ass_export_filter.h"
#include "ass_exporter.h"
#include "ass_export_filter.h"
#include "ass_file.h"
#include "compat.h"
#include "include/aegisub/context.h"
#include <libaegisub/scoped_ptr.h>
#include <algorithm>
#include <memory>
#include <wx/sizer.h>
static inline FilterList::const_iterator filter_list_begin() {
return AssExportFilterChain::GetFilterList()->begin();
@ -62,7 +64,7 @@ void AssExporter::DrawSettings(wxWindow *parent, wxSizer *target_sizer) {
for (auto filter : *AssExportFilterChain::GetFilterList()) {
// Make sure to construct static box sizer first, so it won't overlap
// the controls on wxMac.
wxSizer *box = new wxStaticBoxSizer(wxVERTICAL, parent, filter->GetName());
wxSizer *box = new wxStaticBoxSizer(wxVERTICAL, parent, to_wx(filter->GetName()));
wxWindow *window = filter->GetConfigDialogWindow(parent, c);
if (window) {
box->Add(window, 0, wxEXPAND, 0);
@ -76,18 +78,18 @@ void AssExporter::DrawSettings(wxWindow *parent, wxSizer *target_sizer) {
}
}
void AssExporter::AddFilter(wxString const& name) {
void AssExporter::AddFilter(std::string const& name) {
AssExportFilter *filter = AssExportFilterChain::GetFilter(name);
if (!filter) throw wxString::Format("Filter not found: %s", name);
if (!filter) throw "Filter not found: " + name;
filters.push_back(filter);
}
wxArrayString AssExporter::GetAllFilterNames() const {
wxArrayString names;
std::vector<std::string> AssExporter::GetAllFilterNames() const {
std::vector<std::string> names;
transform(filter_list_begin(), filter_list_end(),
std::back_inserter(names), std::mem_fun(&AssExportFilter::GetName));
back_inserter(names), std::mem_fun(&AssExportFilter::GetName));
return names;
}
@ -102,19 +104,19 @@ AssFile *AssExporter::ExportTransform(wxWindow *export_dialog, bool copy) {
return subs;
}
void AssExporter::Export(wxString const& filename, wxString const& charset, wxWindow *export_dialog) {
agi::scoped_ptr<AssFile> subs(ExportTransform(export_dialog, true));
void AssExporter::Export(agi::fs::path const& filename, std::string const& charset, wxWindow *export_dialog) {
std::unique_ptr<AssFile> subs(ExportTransform(export_dialog, true));
subs->Save(filename, false, false, charset);
}
wxSizer *AssExporter::GetSettingsSizer(wxString const& name) {
wxSizer *AssExporter::GetSettingsSizer(std::string const& name) {
auto pos = Sizers.find(name);
return pos == Sizers.end() ? nullptr : pos->second;
}
wxString const& AssExporter::GetDescription(wxString const& name) const {
std::string const& AssExporter::GetDescription(std::string const& name) const {
AssExportFilter *filter = AssExportFilterChain::GetFilter(name);
if (filter)
return filter->GetDescription();
throw wxString::Format("Filter not found: %s", name);
throw "Filter not found: " + name;
}

View File

@ -32,16 +32,17 @@
/// @ingroup export
///
#include <wx/arrstr.h>
#include <wx/sizer.h>
#include <wx/string.h>
#include <libaegisub/fs_fwd.h>
#include <map>
#include <string>
#include <vector>
class AssExportFilter;
class AssFile;
namespace agi { struct Context; }
class wxSizer;
class wxWindow;
typedef std::vector<AssExportFilter*> FilterList;
@ -49,7 +50,7 @@ class AssExporter {
typedef FilterList::const_iterator filter_iterator;
/// Sizers for configuration panels
std::map<wxString, wxSizer*> Sizers;
std::map<std::string, wxSizer*> Sizers;
/// Filters which will be applied to the subtitles
FilterList filters;
@ -65,11 +66,11 @@ public:
AssExporter(agi::Context *c);
/// Get the names of all registered export filters
wxArrayString GetAllFilterNames() const;
std::vector<std::string> GetAllFilterNames() const;
/// Add the named filter to the list of filters to be run
/// @throws wxString if filter is not found
void AddFilter(wxString const& name);
/// @throws std::string if filter is not found
void AddFilter(std::string const& name);
/// Run all added export filters
/// @param parent_window Parent window the filters should use when opening dialogs
@ -81,7 +82,7 @@ public:
/// @param file Target filename
/// @param charset Target charset
/// @param parent_window Parent window the filters should use when opening dialogs
void Export(wxString const& file, wxString const& charset, wxWindow *parent_window= 0);
void Export(agi::fs::path const& file, std::string const& charset, wxWindow *parent_window= 0);
/// Add configuration panels for all registered filters to the target sizer
/// @param parent Parent window for controls
@ -89,9 +90,9 @@ public:
void DrawSettings(wxWindow *parent, wxSizer *target_sizer);
/// Get the sizer created by DrawSettings for a specific filter
wxSizer *GetSettingsSizer(wxString const& name);
wxSizer *GetSettingsSizer(std::string const& name);
/// Get the description of the named export filter
/// @throws wxString if filter is not found
wxString const& GetDescription(wxString const& name) const;
/// @throws std::string if filter is not found
std::string const& GetDescription(std::string const& name) const;
};

View File

@ -35,21 +35,10 @@
#include "ass_file.h"
#include <algorithm>
#include <boost/algorithm/string/predicate.hpp>
#include <fstream>
#include <inttypes.h>
#include <list>
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/msgdlg.h>
#include "ass_attachment.h"
#include "ass_dialogue.h"
#include "ass_info.h"
#include "ass_style.h"
#include "compat.h"
#include "options.h"
#include "standard_paths.h"
#include "subtitle_format.h"
@ -57,7 +46,17 @@
#include "text_file_writer.h"
#include "utils.h"
#include <libaegisub/dispatch.h>
#include <libaegisub/fs.h>
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/util.h>
#include <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/format.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
#include <list>
namespace std {
template<>
@ -73,16 +72,18 @@ AssFile::AssFile ()
}
AssFile::~AssFile() {
background_delete_clear(Line);
auto copy = new EntryList;
copy->swap(Line);
agi::dispatch::Background().Async([=]{ delete copy; });
}
/// @brief Load generic subs
void AssFile::Load(const wxString &_filename, wxString const& charset) {
const SubtitleFormat *reader = SubtitleFormat::GetReader(_filename);
void AssFile::Load(agi::fs::path const& filename, std::string const& charset) {
const SubtitleFormat *reader = SubtitleFormat::GetReader(filename);
try {
AssFile temp;
reader->ReadFile(&temp, _filename, charset);
reader->ReadFile(&temp, filename, charset);
bool found_style = false;
bool found_dialogue = false;
@ -109,7 +110,7 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) {
// Set general data
loaded = true;
filename = _filename;
this->filename = filename;
// Add comments and set vars
SetScriptInfo("ScriptType", "v4.00+");
@ -124,7 +125,7 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) {
FileOpen(filename);
}
void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxString encoding) {
void AssFile::Save(agi::fs::path const& filename, bool setfilename, bool addToRecent, std::string const& encoding) {
const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
if (!writer)
throw "Unknown file type.";
@ -132,47 +133,38 @@ void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxStri
if (setfilename) {
autosavedCommitId = savedCommitId = commitId;
this->filename = filename;
StandardPaths::SetPathValue("?script", wxFileName(filename).GetPath());
StandardPaths::SetPathValue("?script", filename.parent_path());
}
FileSave();
writer->WriteFile(this, filename, encoding);
if (addToRecent) {
if (addToRecent)
AddToRecent(filename);
}
}
wxString AssFile::AutoSave() {
agi::fs::path AssFile::AutoSave() {
if (!loaded || commitId == autosavedCommitId)
return "";
wxFileName origfile(filename);
wxString path = to_wx(OPT_GET("Path/Auto/Save")->GetString());
if (!path)
path = origfile.GetPath();
path = StandardPaths::DecodePath(path + "/");
auto path = StandardPaths::DecodePath(OPT_GET("Path/Auto/Save")->GetString());
if (path.empty())
path = filename.parent_path();
wxFileName dstpath(path);
if (!dstpath.DirExists())
wxMkdir(path);
agi::fs::CreateDirectory(path);
wxString name = origfile.GetName();
if (!name)
auto name = filename.filename();
if (name.empty())
name = "Untitled";
dstpath.SetFullName(wxString::Format("%s.%s.AUTOSAVE.ass", name, wxDateTime::Now().Format("%Y-%m-%d-%H-%M-%S")));
Save(dstpath.GetFullPath(), false, false);
path /= str(boost::format("%s.%s.AUTOSAVE.ass") % name % agi::util::strftime("%Y-%m-%d-%H-%M-%S"));
Save(path, false, false);
autosavedCommitId = commitId;
return dstpath.GetFullPath();
}
static void write_line(wxString const& line, std::vector<char>& dst) {
wxCharBuffer buffer = (line + "\r\n").utf8_str();
copy(buffer.data(), buffer.data() + buffer.length(), back_inserter(dst));
return path;
}
void AssFile::SaveMemory(std::vector<char> &dst) {
@ -190,9 +182,9 @@ void AssFile::SaveMemory(std::vector<char> &dst) {
for (auto const& line : Line) {
if (group != line.Group()) {
group = line.Group();
write_line(line.GroupHeader(), dst);
boost::push_back(dst, line.GroupHeader() + "\r\n");
}
write_line(line.GetEntryData(), dst);
boost::push_back(dst, line.GetEntryData() + "\r\n");
}
}
@ -205,28 +197,15 @@ bool AssFile::CanSave() const {
}
}
void AssFile::Clear() {
background_delete_clear(Line);
loaded = false;
filename.clear();
UndoStack.clear();
RedoStack.clear();
undoDescription.clear();
}
void AssFile::LoadDefault(bool defline) {
Clear();
// Write headers
Line.push_back(*new AssInfo("Title", "Default Aegisub file"));
Line.push_back(*new AssInfo("ScriptType", "v4.00+"));
Line.push_back(*new AssInfo("WrapStyle", "0"));
Line.push_back(*new AssInfo("ScaledBorderAndShadow", "yes"));
Line.push_back(*new AssInfo("Collisions", "Normal"));
if (!OPT_GET("Subtitle/Default Resolution/Auto")->GetBool()) {
Line.push_back(*new AssInfo("PlayResX", wxString::Format("%" PRId64, OPT_GET("Subtitle/Default Resolution/Width")->GetInt())));
Line.push_back(*new AssInfo("PlayResY", wxString::Format("%" PRId64, OPT_GET("Subtitle/Default Resolution/Height")->GetInt())));
Line.push_back(*new AssInfo("PlayResX", std::to_string(OPT_GET("Subtitle/Default Resolution/Width")->GetInt())));
Line.push_back(*new AssInfo("PlayResY", std::to_string(OPT_GET("Subtitle/Default Resolution/Height")->GetInt())));
}
Line.push_back(*new AssInfo("YCbCr Matrix", "None"));
@ -283,40 +262,32 @@ void AssFile::InsertLine(AssEntry *entry) {
Line.push_front(*entry);
}
void AssFile::InsertAttachment(wxString filename) {
void AssFile::InsertAttachment(agi::fs::path const& filename) {
AssEntryGroup group = ENTRY_GRAPHIC;
wxString ext = filename.Right(4).Lower();
auto ext = boost::to_lower_copy(filename.extension().string());
if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb")
group = ENTRY_FONT;
std::unique_ptr<AssAttachment> newAttach(new AssAttachment(wxFileName(filename).GetFullName(), group));
newAttach->Import(filename);
InsertLine(newAttach.release());
InsertLine(new AssAttachment(filename, group));
}
wxString AssFile::GetScriptInfo(wxString key) const {
key.MakeLower();
std::string AssFile::GetScriptInfo(std::string const& key) const {
for (const auto info : Line | agi::of_type<AssInfo>()) {
if (key == info->Key().Lower())
if (boost::iequals(key, info->Key()))
return info->Value();
}
return "";
}
int AssFile::GetScriptInfoAsInt(wxString const& key) const {
long temp = 0;
GetScriptInfo(key).ToLong(&temp);
return temp;
int AssFile::GetScriptInfoAsInt(std::string const& key) const {
return atoi(GetScriptInfo(key).c_str());
}
void AssFile::SetScriptInfo(wxString const& key, wxString const& value) {
wxString lower_key = key.Lower();
void AssFile::SetScriptInfo(std::string const& key, std::string const& value) {
for (auto info : Line | agi::of_type<AssInfo>()) {
if (lower_key == info->Key().Lower()) {
if (boost::iequals(key, info->Key())) {
if (value.empty())
delete info;
else
@ -366,10 +337,9 @@ AssStyle *AssFile::GetStyle(std::string const& name) {
return nullptr;
}
void AssFile::AddToRecent(wxString const& file) const {
config::mru->Add("Subtitle", from_wx(file));
wxFileName filepath(file);
OPT_SET("Path/Last/Subtitles")->SetString(from_wx(filepath.GetPath()));
void AssFile::AddToRecent(agi::fs::path const& file) const {
config::mru->Add("Subtitle", file);
OPT_SET("Path/Last/Subtitles")->SetString(file.parent_path().string());
}
int AssFile::Commit(wxString const& desc, int type, int amendId, AssEntry *single_line) {

View File

@ -33,10 +33,13 @@
///
#include <boost/container/list.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/intrusive/list.hpp>
#include <set>
#include <vector>
#include <wx/string.h>
#include <libaegisub/fs_fwd.h>
#include <libaegisub/signal.h>
#include "ass_entry.h"
@ -63,7 +66,7 @@ class AssFile {
/// A set of changes has been committed to the file (AssFile::CommitType)
agi::signal::Signal<int, std::set<const AssEntry*> const&> AnnounceCommit;
/// A new file has been opened (filename)
agi::signal::Signal<wxString> FileOpen;
agi::signal::Signal<agi::fs::path> FileOpen;
/// The file is about to be saved
/// This signal is intended for adding metadata such as video filename,
/// frame number, etc. Ideally this would all be done immediately rather
@ -74,7 +77,7 @@ public:
/// The lines in the file
EntryList Line;
/// The filename of this file, if any
wxString filename;
agi::fs::path filename;
/// Is the file loaded?
bool loaded;
@ -84,9 +87,7 @@ public:
~AssFile();
/// Does the file have unsaved changes?
bool IsModified() const {return commitId != savedCommitId; };
/// Clear the file
void Clear();
bool IsModified() const { return commitId != savedCommitId; };
/// @brief Load default file
/// @param defline Add a blank line to the file
@ -94,7 +95,7 @@ public:
/// Add a line to the file at the end of the appropriate section
void InsertLine(AssEntry *line);
/// Attach a file to the ass file
void InsertAttachment(wxString filename);
void InsertAttachment(agi::fs::path const& filename);
/// Get the names of all of the styles available
std::vector<std::string> GetStyles() const;
/// @brief Get a style by name
@ -107,24 +108,24 @@ public:
/// @brief Load from a file
/// @param file File name
/// @param charset Character set of file or empty to autodetect
void Load(const wxString &file, wxString const& charset="");
void Load(agi::fs::path const& file, std::string const& charset="");
/// @brief Save to a file
/// @param file Path to save to
/// @param setfilename Should the filename be changed to the passed path?
/// @param addToRecent Should the file be added to the MRU list?
/// @param encoding Encoding to use, or empty to let the writer decide (which usually means "App/Save Charset")
void Save(wxString file,bool setfilename=false,bool addToRecent=true,const wxString encoding="");
void Save(agi::fs::path const& file, bool setfilename=false, bool addToRecent=true, std::string const& encoding="");
/// @brief Autosave the file if there have been any chances since the last autosave
/// @return File name used or empty if no save was performed
wxString AutoSave();
agi::fs::path AutoSave();
/// @brief Save to a memory buffer. Used for subtitle providers which support it
/// @param[out] dst Destination vector
void SaveMemory(std::vector<char> &dst);
/// Add file name to the MRU list
void AddToRecent(wxString const& file) const;
void AddToRecent(agi::fs::path const& file) const;
/// Can the file be saved in its current format?
bool CanSave() const;
@ -133,11 +134,11 @@ public:
/// @param[in] h Height
void GetResolution(int &w,int &h) const;
/// Get the value in a [Script Info] key as int, or 0 if it is not present
int GetScriptInfoAsInt(wxString const& key) const;
int GetScriptInfoAsInt(std::string const& key) const;
/// Get the value in a [Script Info] key as string.
wxString GetScriptInfo(wxString key) const;
std::string GetScriptInfo(std::string const& key) const;
/// Set the value of a [Script Info] key. Adds it if it doesn't exist.
void SetScriptInfo(wxString const& key, wxString const& value);
void SetScriptInfo(std::string const& key, std::string const& value);
/// Type of changes made in a commit
enum CommitType {

View File

@ -16,21 +16,22 @@
#include "ass_entry.h"
#include <boost/algorithm/string/predicate.hpp>
class AssInfo : public AssEntry {
wxString key;
wxString value;
std::string key;
std::string value;
public:
AssInfo(AssInfo const& o) : key(o.key), value(o.value) { }
AssInfo(wxString const& line) : key(line.BeforeFirst(':').Trim()), value(line.AfterFirst(':').Trim(false)) { }
AssInfo(wxString const& key, wxString const& value) : key(key), value(value) { }
AssInfo(std::string const& key, std::string const& value) : key(key), value(value) { }
AssEntry *Clone() const override { return new AssInfo(*this); }
AssEntryGroup Group() const override { return ENTRY_INFO; }
const wxString GetEntryData() const override { return key + ": " + value; }
wxString GetSSAText() const override { return key.Lower() == "scripttype: v4.00+" ? "ScriptType: v4.00" : GetEntryData(); }
const std::string GetEntryData() const override { return key + ": " + value; }
std::string GetSSAText() const override { return boost::iequals(key, "scripttype: v4.00+") ? "ScriptType: v4.00" : GetEntryData(); }
wxString Key() const { return key; }
wxString Value() const { return value; }
void SetValue(wxString const& new_value) { value = new_value; }
std::string Key() const { return key; }
std::string Value() const { return value; }
void SetValue(std::string const& new_value) { value = new_value; }
};

View File

@ -25,7 +25,6 @@
#include "ass_dialogue.h"
#include "ass_file.h"
#include "compat.h"
#include "include/aegisub/context.h"
#include "selection_controller.h"
@ -161,12 +160,12 @@ void AssKaraoke::ParseSyllables(AssDialogue *line, Syllable &syl) {
syls.push_back(syl);
}
wxString AssKaraoke::GetText() const {
wxString text;
std::string AssKaraoke::GetText() const {
std::string text;
text.reserve(size() * 10);
for (auto const& syl : syls)
text += to_wx(syl.GetText(true));
text += syl.GetText(true);
return text;
}
@ -301,7 +300,7 @@ void AssKaraoke::SplitLines(std::set<AssDialogue*> const& lines, agi::Context *c
new_line->Start = syl.start_time;
new_line->End = syl.start_time + syl.duration;
new_line->Text = to_wx(syl.GetText(false));
new_line->Text = syl.GetText(false);
c->ass->Line.insert(it, *new_line);

View File

@ -25,8 +25,6 @@
#include <string>
#include <vector>
#include <wx/string.h>
#include <libaegisub/signal.h>
namespace agi { struct Context; }
@ -84,7 +82,7 @@ public:
size_t size() const { return syls.size(); }
/// Get the line's text with k tags
wxString GetText() const;
std::string GetText() const;
/// Get the karaoke tag type used, with leading slash
/// @returns "\k", "\kf", or "\ko"

View File

@ -24,8 +24,9 @@
#include "subtitle_format.h"
#include <algorithm>
#include <wx/log.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
AssParser::AssParser(AssFile *target, int version)
: target(target)
@ -39,8 +40,8 @@ AssParser::AssParser(AssFile *target, int version)
AssParser::~AssParser() {
}
void AssParser::ParseAttachmentLine(wxString const& data) {
bool is_filename = data.StartsWith("fontname: ") || data.StartsWith("filename: ");
void AssParser::ParseAttachmentLine(std::string const& data) {
bool is_filename = boost::starts_with(data, "fontname: ") || boost::starts_with(data, "filename: ");
bool valid_data = data.size() > 0 && data.size() <= 80;
for (auto byte : data) {
@ -60,59 +61,59 @@ void AssParser::ParseAttachmentLine(wxString const& data) {
attach->AddData(data);
// Done building
if (data.Length() < 80) {
if (data.size() < 80) {
attach->Finish();
InsertLine(attach.release());
}
}
}
void AssParser::ParseScriptInfoLine(wxString const& data) {
if (data.StartsWith(";")) {
void AssParser::ParseScriptInfoLine(std::string const& data) {
if (boost::starts_with(data, ";")) {
// Skip stupid comments added by other programs
// Of course, we'll add our own in place later... ;)
return;
}
if (data.StartsWith("ScriptType:")) {
wxString versionString = data.Mid(11).Trim(true).Trim(false).Lower();
int trueVersion;
if (versionString == "v4.00")
trueVersion = 0;
else if (versionString == "v4.00+")
trueVersion = 1;
if (boost::starts_with(data, "ScriptType:")) {
std::string version_str = data.substr(11);
boost::trim(version_str);
boost::to_lower(version_str);
if (version_str == "v4.00")
version = 0;
else if (version_str == "v4.00+")
version = 1;
else
throw SubtitleFormatParseError("Unknown SSA file format version", 0);
if (trueVersion != version) {
wxLogMessage("Warning: File has the wrong extension.");
version = trueVersion;
}
}
InsertLine(new AssInfo(data));
size_t pos = data.find(':');
if (pos == data.npos) return;
InsertLine(new AssInfo(data.substr(0, pos), boost::trim_left_copy(data.substr(pos + 1))));
}
void AssParser::ParseEventLine(wxString const& data) {
if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:"))
void AssParser::ParseEventLine(std::string const& data) {
if (boost::starts_with(data, "Dialogue:") || boost::starts_with(data, "Comment:"))
InsertLine(new AssDialogue(data));
}
void AssParser::ParseStyleLine(wxString const& data) {
if (data.StartsWith("Style:"))
void AssParser::ParseStyleLine(std::string const& data) {
if (boost::starts_with(data, "Style:"))
InsertLine(new AssStyle(data, version));
}
void AssParser::ParseFontLine(wxString const& data) {
if (data.StartsWith("fontname: "))
attach.reset(new AssAttachment(data.Mid(10), ENTRY_FONT));
void AssParser::ParseFontLine(std::string const& data) {
if (boost::starts_with(data, "fontname: "))
attach.reset(new AssAttachment(data.substr(10), ENTRY_FONT));
}
void AssParser::ParseGraphicsLine(wxString const& data) {
if (data.StartsWith("filename: "))
attach.reset(new AssAttachment(data.Mid(10), ENTRY_GRAPHIC));
void AssParser::ParseGraphicsLine(std::string const& data) {
if (boost::starts_with(data, "filename: "))
attach.reset(new AssAttachment(data.substr(10), ENTRY_GRAPHIC));
}
void AssParser::AddLine(wxString const& data) {
void AssParser::AddLine(std::string const& data) {
// Special-case for attachments since a line could theoretically be both a
// valid attachment data line and a valid section header, and if an
// attachment is in progress it needs to be treated as that
@ -124,10 +125,10 @@ void AssParser::AddLine(wxString const& data) {
if (data.empty()) return;
// Section header
if (data[0] == '[' && data.Last() == ']') {
if (data[0] == '[' && data.back() == ']') {
// Ugly hacks to allow intermixed v4 and v4+ style sections
const wxString low = data.Lower();
wxString header = data;
const std::string low = boost::to_lower_copy(data);
std::string header = data;
if (low == "[v4 styles]") {
header = "[V4+ Styles]";
version = 0;

View File

@ -16,8 +16,6 @@
#include <map>
#include <memory>
#include <wx/string.h>
#include "ass_entry.h"
class AssAttachment;
@ -27,20 +25,21 @@ class AssParser {
AssFile *target;
int version;
std::unique_ptr<AssAttachment> attach;
void (AssParser::*state)(wxString const&);
void (AssParser::*state)(std::string const&);
std::array<AssEntry*, ENTRY_GROUP_MAX> insertion_positions;
void InsertLine(AssEntry *entry);
void ParseAttachmentLine(wxString const& data);
void ParseEventLine(wxString const& data);
void ParseStyleLine(wxString const& data);
void ParseScriptInfoLine(wxString const& data);
void ParseFontLine(wxString const& data);
void ParseGraphicsLine(wxString const& data);
void UnknownLine(wxString const&) { }
void ParseAttachmentLine(std::string const& data);
void ParseEventLine(std::string const& data);
void ParseStyleLine(std::string const& data);
void ParseScriptInfoLine(std::string const& data);
void ParseFontLine(std::string const& data);
void ParseGraphicsLine(std::string const& data);
void UnknownLine(std::string const&) { }
public:
AssParser(AssFile *target, int version);
~AssParser();
void AddLine(wxString const& data);
void AddLine(std::string const& data);
};

View File

@ -35,13 +35,15 @@
#include "config.h"
#include "ass_style.h"
#include "compat.h"
#include "subtitle_format.h"
#include "utils.h"
#include <libaegisub/split.h>
#include <algorithm>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <cctype>
@ -74,42 +76,28 @@ AssStyle::AssStyle()
namespace {
class parser {
typedef boost::iterator_range<std::string::const_iterator> string_range;
string_range str;
std::vector<string_range> tkns;
size_t tkn_idx;
boost::split_iterator<agi::StringRange::const_iterator> pos;
string_range next_tok() {
if (tkn_idx >= tkns.size())
std::string next_tok() {
if (pos.eof())
throw SubtitleFormatParseError("Malformed style: not enough fields", 0);
return trim_copy(tkns[tkn_idx++]);
return agi::str(trim_copy(*pos++));
}
public:
parser(std::string const& str)
: tkn_idx(0)
{
auto pos = find(str.begin(), str.end(), ':');
if (pos != str.end()) {
this->str = string_range(pos + 1, str.end());
split(tkns, this->str, [](char c) { return c == ','; });
}
parser(std::string const& str) {
auto colon = find(str.begin(), str.end(), ':');
if (colon != str.end())
pos = agi::Split(agi::StringRange(colon + 1, str.end()), ',');
}
void check_done() const {
if (tkn_idx != tkns.size())
if (!pos.eof())
throw SubtitleFormatParseError("Malformed style: too many fields", 0);
}
std::string next_str() {
auto tkn = next_tok();
return std::string(begin(tkn), end(tkn));
}
agi::Color next_color() {
auto tkn = next_tok();
return std::string(begin(tkn), end(tkn));
}
std::string next_str() { return next_tok(); }
agi::Color next_color() { return next_tok(); }
int next_int() {
try {
@ -130,13 +118,13 @@ public:
}
void skip_token() {
++tkn_idx;
if (!pos.eof())
++pos;
}
};
}
AssStyle::AssStyle(wxString const& rawData, int version) {
std::string str(from_wx(rawData));
AssStyle::AssStyle(std::string const& str, int version) {
parser p(str);
name = p.next_str();
@ -211,28 +199,28 @@ void AssStyle::UpdateData() {
replace(name.begin(), name.end(), ',', ';');
replace(font.begin(), font.end(), ',', ';');
data = wxString::Format("Style: %s,%s,%g,%s,%s,%s,%s,%d,%d,%d,%d,%g,%g,%g,%g,%d,%g,%g,%i,%i,%i,%i,%i",
to_wx(name), to_wx(font), fontsize,
primary.GetAssStyleFormatted(),
secondary.GetAssStyleFormatted(),
outline.GetAssStyleFormatted(),
shadow.GetAssStyleFormatted(),
(bold? -1 : 0), (italic ? -1 : 0),
(underline?-1:0),(strikeout?-1:0),
scalex,scaley,spacing,angle,
borderstyle,outline_w,shadow_w,alignment,
Margin[0],Margin[1],Margin[2],encoding);
data = str(boost::format("Style: %s,%s,%g,%s,%s,%s,%s,%d,%d,%d,%d,%g,%g,%g,%g,%d,%g,%g,%i,%i,%i,%i,%i")
% name % font % fontsize
% primary.GetAssStyleFormatted()
% secondary.GetAssStyleFormatted()
% outline.GetAssStyleFormatted()
% shadow.GetAssStyleFormatted()
% (bold? -1 : 0) % (italic ? -1 : 0)
% (underline ? -1 : 0) % (strikeout ? -1 : 0)
% scalex % scaley % spacing % angle
% borderstyle % outline_w % shadow_w % alignment
% Margin[0] % Margin[1] % Margin[2] % encoding);
}
wxString AssStyle::GetSSAText() const {
return wxString::Format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i",
to_wx(name), to_wx(font), fontsize,
primary.GetSsaFormatted(),
secondary.GetSsaFormatted(),
shadow.GetSsaFormatted(),
(bold? -1 : 0), (italic ? -1 : 0),
borderstyle,outline_w,shadow_w,AssToSsa(alignment),
Margin[0],Margin[1],Margin[2],encoding);
std::string AssStyle::GetSSAText() const {
return str(boost::format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i")
% name % font % fontsize
% primary.GetSsaFormatted()
% secondary.GetSsaFormatted()
% shadow.GetSsaFormatted()
% (bold? -1 : 0) % (italic ? -1 : 0)
% borderstyle % outline_w % shadow_w % AssToSsa(alignment)
% Margin[0] % Margin[1] % Margin[2] % encoding);
}
AssEntry *AssStyle::Clone() const {

View File

@ -40,7 +40,7 @@
#include <wx/arrstr.h>
class AssStyle : public AssEntry {
wxString data;
std::string data;
public:
std::string name; ///< Name of the style; must be case-insensitively unique within a file despite being case-sensitive
@ -75,10 +75,10 @@ public:
static void GetEncodings(wxArrayString &encodingStrings);
AssStyle();
AssStyle(wxString const& data, int version=1);
AssStyle(std::string const& data, int version=1);
const wxString GetEntryData() const override { return data; }
wxString GetSSAText() const override;
const std::string GetEntryData() const override { return data; }
std::string GetSSAText() const override;
AssEntryGroup Group() const override { return ENTRY_STYLE; }
AssEntry *Clone() const override;

View File

@ -42,8 +42,10 @@
#include "text_file_writer.h"
#include "utils.h"
#include <libaegisub/fs.h>
#include <boost/algorithm/string/predicate.hpp>
#include <functional>
#include <boost/filesystem.hpp>
AssStyleStorage::~AssStyleStorage() {
delete_clear(style);
@ -52,16 +54,14 @@ AssStyleStorage::~AssStyleStorage() {
void AssStyleStorage::Save() const {
if (storage_name.empty()) return;
wxString dirname = StandardPaths::DecodePath("?user/catalog/");
if (!wxDirExists(dirname) && !wxMkdir(dirname))
throw "Failed creating directory for style catalogs";
agi::fs::CreateDirectory(StandardPaths::DecodePath("?user/catalog/"));
TextFileWriter file(StandardPaths::DecodePath("?user/catalog/" + storage_name + ".sty"), "UTF-8");
for (const AssStyle *cur : style)
file.WriteLineToFile(cur->GetEntryData());
}
void AssStyleStorage::Load(wxString const& name) {
void AssStyleStorage::Load(std::string const& name) {
storage_name = name;
Clear();
@ -69,17 +69,14 @@ void AssStyleStorage::Load(wxString const& name) {
TextFileReader file(StandardPaths::DecodePath("?user/catalog/" + name + ".sty"), "UTF-8");
while (file.HasMoreLines()) {
wxString data = file.ReadLineFromFile();
if (data.StartsWith("Style:")) {
try {
style.push_back(new AssStyle(data));
} catch(...) {
/* just ignore invalid lines for now */
}
try {
style.push_back(new AssStyle(file.ReadLineFromFile()));
} catch(...) {
/* just ignore invalid lines for now */
}
}
}
catch (agi::FileNotAccessibleError const&) {
catch (agi::fs::FileNotAccessible const&) {
// Just treat a missing file as an empty file
}
}

View File

@ -36,12 +36,10 @@
#include <string>
#include <vector>
#include <wx/string.h>
class AssStyle;
class AssStyleStorage {
wxString storage_name;
std::string storage_name;
std::deque<AssStyle*> style;
public:
@ -77,5 +75,5 @@ public:
/// Load stored styles from a file
/// @param name Catalog name (note: not file name)
void Load(wxString const& name);
void Load(std::string const& name);
};

View File

@ -1,29 +1,16 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 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.
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// 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/
@ -36,61 +23,64 @@
#include "ass_time.h"
#include <algorithm>
#include <cmath>
#include <wx/tokenzr.h>
#include "utils.h"
#include <libaegisub/util.h>
#include <algorithm>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/format.hpp>
#include <boost/range/adaptor/filtered.hpp>
AssTime::AssTime(int time) : time(mid(0, time, 10 * 60 * 60 * 1000 - 1)) { }
AssTime::AssTime(wxString const& text)
AssTime::AssTime(std::string const& text)
: time(0)
{
size_t pos = 0, end = 0;
int colons = text.Freq(':');
// Set start so that there are only two colons at most
for (; colons > 2; --colons) pos = text.find(':', pos) + 1;
// Hours
if (colons == 2) {
while (text[end++] != ':') { }
time += AegiStringToInt(text, pos, end) * 60 * 60 * 1000;
pos = end;
int after_decimal = -1;
int current = 0;
for (char c : text | boost::adaptors::filtered(boost::is_any_of(":0123456789."))) {
if (c == ':') {
time = time * 60 + current;
current = 0;
}
else if (c == '.') {
time = (time * 60 + current) * 1000;
current = 0;
after_decimal = 100;
}
else if (after_decimal < 0) {
current *= 10;
current += c - '0';
}
else {
time += (c - '0') * after_decimal;
after_decimal /= 10;
}
}
// Minutes
if (colons >= 1) {
while (text[end++] != ':') { }
time += AegiStringToInt(text, pos, end) * 60 * 1000;
}
// Milliseconds (includes seconds)
time += AegiStringToFix(text, 3, end, text.size());
// Never saw a decimal, so convert now to ms
if (after_decimal < 0)
time = (time * 60 + current) * 1000;
// Limit to the valid range
time = mid(0, time, 10 * 60 * 60 * 1000 - 1);
}
wxString AssTime::GetAssFormated(bool msPrecision) const {
wxChar ret[] = {
'0' + GetTimeHours(),
':',
'0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10),
'0' + (time % (10 * 60 * 1000)) / (60 * 1000),
':',
'0' + (time % (60 * 1000)) / (1000 * 10),
'0' + (time % (10 * 1000)) / 1000,
'.',
'0' + (time % 1000) / 100,
'0' + (time % 100) / 10,
'0' + time % 10
};
return wxString(ret, msPrecision ? 11 : 10);
std::string AssTime::GetAssFormated(bool msPrecision) const {
std::string ret(10 + msPrecision, ':');
ret[0] = '0' + GetTimeHours();
ret[2] = '0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10);
ret[3] = '0' + (time % (10 * 60 * 1000)) / (60 * 1000);
ret[5] = '0' + (time % (60 * 1000)) / (1000 * 10);
ret[6] = '0' + (time % (10 * 1000)) / 1000;
ret[7] = '.';
ret[8] = '0' + (time % 1000) / 100;
ret[9] = '0' + (time % 100) / 10;
if (msPrecision)
ret[10] = '0' + time % 10;
return ret;
}
int AssTime::GetTimeHours() const { return time / 3600000; }
@ -99,25 +89,27 @@ int AssTime::GetTimeSeconds() const { return (time % 60000) / 1000; }
int AssTime::GetTimeMiliseconds() const { return (time % 1000); }
int AssTime::GetTimeCentiseconds() const { return (time % 1000) / 10; }
SmpteFormatter::SmpteFormatter(agi::vfr::Framerate fps, char sep)
SmpteFormatter::SmpteFormatter(agi::vfr::Framerate fps, std::string const& sep)
: fps(fps)
, sep(sep)
{
}
wxString SmpteFormatter::ToSMPTE(AssTime time) const {
std::string SmpteFormatter::ToSMPTE(AssTime time) const {
int h=0, m=0, s=0, f=0;
fps.SmpteAtTime(time, &h, &m, &s, &f);
return wxString::Format("%02d%c%02d%c%02d%c%02d", h, sep, m, sep, s, sep, f);
return str(boost::format("%02d%s%02d%s%02d%c%02d") % h % sep % m % sep % s % sep % f);
}
AssTime SmpteFormatter::FromSMPTE(wxString const& str) const {
long h, m, s, f;
wxArrayString toks = wxStringTokenize(str, sep);
AssTime SmpteFormatter::FromSMPTE(std::string const& str) const {
std::vector<std::string> toks;
boost::split(toks, str, boost::is_any_of(sep));
if (toks.size() != 4) return 0;
toks[0].ToLong(&h);
toks[1].ToLong(&m);
toks[2].ToLong(&s);
toks[3].ToLong(&f);
int h, m, s, f;
agi::util::try_parse(toks[0], &h);
agi::util::try_parse(toks[1], &m);
agi::util::try_parse(toks[2], &s);
agi::util::try_parse(toks[3], &f);
return fps.TimeAtSmpte(h, m, s, f);
}

View File

@ -1,29 +1,16 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 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.
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// 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/
@ -34,7 +21,7 @@
#pragma once
#include <wx/string.h>
#include <string>
#include <libaegisub/vfr.h>
@ -44,7 +31,7 @@ class AssTime {
public:
AssTime(int ms = 0);
AssTime(wxString const& text);
AssTime(std::string const& text);
/// Get millisecond, rounded to centisecond precision
operator int() const { return time / 10 * 10; }
@ -57,7 +44,7 @@ public:
/// Return the time as a string
/// @param ms Use milliseconds precision, for non-ASS formats
wxString GetAssFormated(bool ms=false) const;
std::string GetAssFormated(bool ms=false) const;
};
/// @class SmpteFormatter
@ -66,13 +53,13 @@ class SmpteFormatter {
/// Frame rate to use
agi::vfr::Framerate fps;
/// Separator character
char sep;
std::string sep;
public:
SmpteFormatter(agi::vfr::Framerate fps, char sep=':');
SmpteFormatter(agi::vfr::Framerate fps, std::string const& sep=":");
/// Convert an AssTime to a SMPTE timecode
wxString ToSMPTE(AssTime time) const;
std::string ToSMPTE(AssTime time) const;
/// Convert a SMPTE timecode to an AssTime
AssTime FromSMPTE(wxString const& str) const;
AssTime FromSMPTE(std::string const& str) const;
};

View File

@ -34,14 +34,9 @@
#include "config.h"
#include <algorithm>
#include <wx/filename.h>
#include <libaegisub/io.h>
#include "audio_controller.h"
#include "ass_file.h"
#include "audio_controller.h"
#include "audio_provider_dummy.h"
#include "audio_timing.h"
#include "compat.h"
@ -51,10 +46,14 @@
#include "pen.h"
#include "options.h"
#include "selection_controller.h"
#include "standard_paths.h"
#include "utils.h"
#include "video_context.h"
#include <libaegisub/io.h>
#include <libaegisub/path.h>
#include <algorithm>
AudioController::AudioController(agi::Context *context)
: context(context)
, subtitle_save_slot(context->ass->AddFileSaveListener(&AudioController::OnSubtitlesSave, this))
@ -81,13 +80,11 @@ AudioController::AudioController(agi::Context *context)
#endif
}
AudioController::~AudioController()
{
CloseAudio();
}
void AudioController::OnPlaybackTimer(wxTimerEvent &)
{
int64_t pos = player->GetCurrentPosition();
@ -105,7 +102,6 @@ void AudioController::OnPlaybackTimer(wxTimerEvent &)
}
}
#ifdef wxHAS_POWER_EVENTS
void AudioController::OnComputerSuspending(wxPowerEvent &)
{
@ -114,7 +110,6 @@ void AudioController::OnComputerSuspending(wxPowerEvent &)
player = 0;
}
void AudioController::OnComputerResuming(wxPowerEvent &)
{
if (provider)
@ -154,25 +149,25 @@ void AudioController::OnAudioProviderChanged()
{
if (IsAudioOpen())
// url is cloned because CloseAudio clears it and OpenAudio takes a const reference
OpenAudio(audio_url.Clone());
OpenAudio(agi::fs::path(audio_url));
}
void AudioController::OpenAudio(const wxString &url)
void AudioController::OpenAudio(agi::fs::path const& url)
{
if (!url)
if (url.empty())
throw agi::InternalError("AudioController::OpenAudio() was passed an empty string. This must not happen.", 0);
AudioProvider *new_provider = 0;
try {
new_provider = AudioProviderFactory::GetProvider(url);
StandardPaths::SetPathValue("?audio", wxFileName(url).GetPath());
config::path->SetToken("?audio", url);
}
catch (agi::UserCancelException const&) {
throw;
}
catch (...) {
config::mru->Remove("Audio", from_wx(url));
config::mru->Remove("Audio", url);
throw;
}
@ -192,11 +187,10 @@ void AudioController::OpenAudio(const wxString &url)
audio_url = url;
config::mru->Add("Audio", from_wx(url));
config::mru->Add("Audio", url);
try
{
// Tell listeners about this.
AnnounceAudioOpen(provider);
}
catch (...)
@ -206,7 +200,6 @@ void AudioController::OpenAudio(const wxString &url)
}
}
void AudioController::CloseAudio()
{
Stop();
@ -218,7 +211,7 @@ void AudioController::CloseAudio()
audio_url.clear();
StandardPaths::SetPathValue("?audio", "");
config::path->SetToken("?audio", "");
AnnounceAudioClose();
}
@ -229,12 +222,6 @@ bool AudioController::IsAudioOpen() const
return player && provider;
}
wxString AudioController::GetAudioURL() const
{
return audio_url;
}
void AudioController::SetTimingController(AudioTimingController *new_controller)
{
if (timing_controller.get() != new_controller) {
@ -259,13 +246,9 @@ void AudioController::OnTimingControllerUpdatedPrimaryRange()
void AudioController::OnSubtitlesSave()
{
if (IsAudioOpen())
{
context->ass->SetScriptInfo("Audio URI", MakeRelativePath(audio_url, context->ass->filename));
}
context->ass->SetScriptInfo("Audio URI", config::path->MakeRelative(audio_url, "?script").generic_string());
else
{
context->ass->SetScriptInfo("Audio URI", "");
}
}
void AudioController::PlayRange(const TimeRange &range)
@ -402,13 +385,13 @@ int64_t AudioController::MillisecondsFromSamples(int64_t samples) const
return millisamples / sr;
}
void AudioController::SaveClip(wxString const& filename, TimeRange const& range) const
void AudioController::SaveClip(agi::fs::path const& filename, TimeRange const& range) const
{
int64_t start_sample = SamplesFromMilliseconds(range.begin());
int64_t end_sample = SamplesFromMilliseconds(range.end());
if (filename.empty() || start_sample > provider->GetNumSamples() || range.length() == 0) return;
agi::io::Save outfile(from_wx(filename), true);
agi::io::Save outfile(filename, true);
std::ofstream& out(outfile.Get());
size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels();

View File

@ -33,20 +33,16 @@
#pragma once
#include <cassert>
#include <boost/filesystem/path.hpp>
#include <cstdint>
#include <memory>
#include <set>
#include <vector>
#include <wx/event.h>
#include <wx/string.h>
#include <wx/timer.h>
#include <wx/pen.h>
#include <wx/power.h>
#include <libaegisub/exception.h>
#include <libaegisub/scoped_ptr.h>
#include <libaegisub/fs_fwd.h>
#include <libaegisub/signal.h>
class AudioPlayer;
@ -96,10 +92,10 @@ class AudioController : public wxEvtHandler {
AudioProvider *provider;
/// The current timing mode, if any; owned by the audio controller
agi::scoped_ptr<AudioTimingController> timing_controller;
std::unique_ptr<AudioTimingController> timing_controller;
/// The URL of the currently open audio, if any
wxString audio_url;
agi::fs::path audio_url;
enum PlaybackMode {
@ -157,13 +153,9 @@ public:
/// @brief Destructor
~AudioController();
/// @brief Open an audio stream
/// @param url URL of the stream to open
///
/// The URL can either be a plain filename (with no qualifiers) or one
/// recognised by various providers.
void OpenAudio(const wxString &url);
void OpenAudio(agi::fs::path const& url);
/// @brief Closes the current audio stream
void CloseAudio();
@ -177,7 +169,7 @@ public:
///
/// The returned URL can be passed into OpenAudio() later to open the same
/// stream again.
wxString GetAudioURL() const;
agi::fs::path GetAudioURL() const { return audio_url; }
/// @brief Start or restart audio playback, playing a range
@ -263,7 +255,7 @@ public:
/// @brief Save a portion of the decoded loaded audio to a wav file
/// @param filename File to save to
/// @param range Time range to save
void SaveClip(wxString const& filename, TimeRange const& range) const;
void SaveClip(agi::fs::path const& filename, TimeRange const& range) const;
DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener)
DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener)

View File

@ -963,7 +963,7 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
if (show_time)
{
AssTime new_label_time = TimeFromAbsoluteX(track_cursor_pos);
track_cursor_label = new_label_time.GetAssFormated();
track_cursor_label = to_wx(new_label_time.GetAssFormated());
track_cursor_label_rect.x += new_pos - old_pos;
RefreshRect(track_cursor_label_rect, false);
}

View File

@ -44,7 +44,6 @@
#include "audio_player_pulse.h"
#include "audio_controller.h"
#include "compat.h"
#include "options.h"
AudioPlayer::AudioPlayer(AudioProvider *provider)

View File

@ -369,8 +369,7 @@ AlsaPlayer::AlsaPlayer(AudioProvider *provider)
{
ps->provider = provider;
wxString device_name = to_wx(OPT_GET("Player/Audio/ALSA/Device")->GetString());
ps->device_name = std::string(device_name.utf8_str());
ps->device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString();
if (pthread_create(&thread, 0, &playback_thread, ps.get()) != 0)
throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0);

View File

@ -75,7 +75,7 @@ void OSSPlayer::OpenStream()
// Open device
wxString device = to_wx(OPT_GET("Player/Audio/OSS/Device")->GetString());
dspdev = ::open(device.mb_str(wxConvUTF8), O_WRONLY, 0);
dspdev = ::open(device.utf8_str(), O_WRONLY, 0);
if (dspdev < 0) {
throw OSSError("OSS player: opening device failed", 0);
}

View File

@ -32,11 +32,8 @@
/// @ingroup audio_input
///
#include "config.h"
#include <cstdint>
#include "audio_provider_avs.h"
#include "audio_provider_convert.h"
#include "audio_provider_dummy.h"
@ -47,13 +44,13 @@
#include "audio_provider_ram.h"
#include "audio_controller.h"
#include "compat.h"
#include "dialog_progress.h"
#include "frame_main.h"
#include "main.h"
#include "options.h"
#include "utils.h"
#include <libaegisub/fs.h>
#include <libaegisub/log.h>
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
@ -126,7 +123,7 @@ struct provider_creator {
LOG_I("audio_provider") << "Using audio provider: " << name;
return provider;
}
catch (agi::FileNotFoundError const& err) {
catch (agi::fs::FileNotFound const& err) {
LOG_D("audio_provider") << err.GetChainedMessage();
msg += name + ": " + err.GetMessage() + " not found.\n";
}
@ -147,7 +144,7 @@ struct provider_creator {
};
}
AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename) {
AudioProvider *AudioProviderFactory::GetProvider(agi::fs::path const& filename) {
provider_creator creator;
AudioProvider *provider = nullptr;
@ -172,7 +169,7 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename) {
throw agi::AudioProviderOpenError(creator.msg, 0);
if (creator.found_file)
throw agi::AudioDataNotFoundError(creator.msg, 0);
throw agi::FileNotFoundError(from_wx(filename));
throw agi::fs::FileNotFound(filename);
}
bool needsCache = provider->NeedsCache();
@ -206,4 +203,4 @@ void AudioProviderFactory::RegisterProviders() {
#endif
}
template<> AudioProviderFactory::map *FactoryBase<AudioProvider *(*)(wxString)>::classes = nullptr;
template<> AudioProviderFactory::map *FactoryBase<AudioProvider *(*)(agi::fs::path)>::classes = nullptr;

View File

@ -38,38 +38,38 @@
#include "audio_provider_avs.h"
#include "audio_controller.h"
#include "charset_conv.h"
#include "compat.h"
#include "options.h"
#include "standard_paths.h"
#include "utils.h"
#include <wx/filename.h>
#include <libaegisub/access.h>
#include <libaegisub/charset_conv.h>
#include <libaegisub/fs.h>
AvisynthAudioProvider::AvisynthAudioProvider(wxString filename) {
#include <mutex>
AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) {
this->filename = filename;
wxMutexLocker lock(avs_wrapper.GetMutex());
agi::acs::CheckFileRead(filename);
wxFileName fn(filename);
if (!fn.FileExists())
throw agi::FileNotFoundError(from_wx(filename));
std::lock_guard<std::mutex> lock(avs_wrapper.GetMutex());
try {
IScriptEnvironment *env = avs_wrapper.GetEnv();
// Include
if (filename.EndsWith(".avs"))
LoadFromClip(env->Invoke("Import", env->SaveString(fn.GetShortPath().mb_str(csConvLocal))));
if (agi::fs::HasExtension(filename, "avs"))
LoadFromClip(env->Invoke("Import", env->SaveString(agi::fs::ShortName(filename).c_str())));
// Use DirectShowSource
else {
const char * argnames[3] = { 0, "video", "audio" };
AVSValue args[3] = { env->SaveString(fn.GetShortPath().mb_str(csConvLocal)), false, true };
AVSValue args[3] = { env->SaveString(agi::fs::ShortName(filename).c_str()), false, true };
// Load DirectShowSource.dll from app dir if it exists
wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll"));
if (dsspath.FileExists())
env->Invoke("LoadPlugin", env->SaveString(dsspath.GetShortPath().mb_str(csConvLocal)));
agi::fs::path dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll"));
if (agi::fs::FileExists(dsspath))
env->Invoke("LoadPlugin", env->SaveString(agi::fs::ShortName(dsspath).c_str()));
// Load audio with DSS if it exists
if (env->FunctionExists("DirectShowSource"))

View File

@ -46,7 +46,7 @@ class AvisynthAudioProvider : public AudioProvider {
void FillBuffer(void *buf, int64_t start, int64_t count) const;
public:
AvisynthAudioProvider(wxString filename);
AvisynthAudioProvider(agi::fs::path const& filename);
bool AreSamplesNativeEndian() const { return true; }
bool NeedsCache() const { return true; }

View File

@ -46,7 +46,7 @@ public:
}
bool AreSamplesNativeEndian() const { return true; }
wxString GetFilename() const { return source->GetFilename(); }
agi::fs::path GetFilename() const { return source->GetFilename(); }
};
/// Anything integral -> 16 bit signed machine-endian audio converter

View File

@ -37,7 +37,9 @@
#include "audio_provider_dummy.h"
#include "utils.h"
#include <wx/uri.h>
#include <libaegisub/fs.h>
#include <boost/algorithm/string/predicate.hpp>
/*
* scheme ::= "dummy-audio" ":" signal-specifier "?" signal-parameters
@ -59,13 +61,11 @@
* in every channel even if one would be LFE.
* "ln", length of signal in samples. ln/sr gives signal length in seconds.
*/
DummyAudioProvider::DummyAudioProvider(wxString uri)
{
wxURI parsed(uri);
if (parsed.GetScheme() != "dummy-audio")
throw agi::FileNotFoundError("Not a dummy audio URI");
DummyAudioProvider::DummyAudioProvider(agi::fs::path const& uri) {
if (!boost::starts_with(uri.string(), "dummy-audio:"))
throw agi::fs::FileNotFound(std::string("Not a dummy audio URI"));
noise = parsed.GetPath() == "noise";
noise = boost::contains(uri.string(), ":noise?");
channels = 1;
sample_rate = 44100;
bytes_per_sample = 2;

View File

@ -39,7 +39,7 @@ class DummyAudioProvider : public AudioProvider {
void FillBuffer(void *buf, int64_t start, int64_t count) const;
public:
DummyAudioProvider(wxString uri);
DummyAudioProvider(agi::fs::path const& uri);
bool AreSamplesNativeEndian() const { return true; }
};

View File

@ -35,22 +35,18 @@
#include "config.h"
#ifdef WITH_FFMS2
#ifdef WIN32
#include <objbase.h>
#endif
#include <map>
#include "audio_provider_ffmpegsource.h"
#include "audio_controller.h"
#include "compat.h"
#include "options.h"
#include <libaegisub/fs.h>
#include <map>
/// @brief Constructor
/// @param filename The filename to open
FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename) try
FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(agi::fs::path const& filename) try
: AudioSource(nullptr, FFMS_DestroyAudioSource)
{
ErrInfo.Buffer = FFMSErrMsg;
@ -61,26 +57,24 @@ FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename) try
LoadAudio(filename);
}
catch (wxString const& err) {
throw agi::AudioProviderOpenError(from_wx(err), 0);
catch (std::string const& err) {
throw agi::AudioProviderOpenError(err, 0);
}
catch (const char *err) {
throw agi::AudioProviderOpenError(err, 0);
}
void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
wxString FileNameShort = wxFileName(filename).GetShortPath();
FFMS_Indexer *Indexer = FFMS_CreateIndexer(FileNameShort.utf8_str(), &ErrInfo);
void FFmpegSourceAudioProvider::LoadAudio(agi::fs::path const& filename) {
FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.string().c_str(), &ErrInfo);
if (!Indexer) {
if (ErrInfo.SubType == FFMS_ERROR_FILE_READ)
throw agi::FileNotFoundError(ErrInfo.Buffer);
throw agi::fs::FileNotFound(std::string(ErrInfo.Buffer));
else
throw agi::AudioDataNotFoundError(ErrInfo.Buffer, 0);
}
std::map<int,wxString> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
if (TrackList.size() <= 0)
std::map<int, std::string> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
if (TrackList.empty())
throw agi::AudioDataNotFoundError("no audio tracks found", 0);
// initialize the track number to an invalid value so we can detect later on
@ -94,13 +88,13 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
}
// generate a name for the cache file
wxString CacheName = GetCacheFilename(filename);
agi::fs::path CacheName = GetCacheFilename(filename);
// try to read index
agi::scoped_holder<FFMS_Index*, void (FFMS_CC*)(FFMS_Index*)>
Index(FFMS_ReadIndex(CacheName.utf8_str(), &ErrInfo), FFMS_DestroyIndex);
Index(FFMS_ReadIndex(CacheName.string().c_str(), &ErrInfo), FFMS_DestroyIndex);
if (Index && FFMS_IndexBelongsToFile(Index, FileNameShort.utf8_str(), &ErrInfo))
if (Index && FFMS_IndexBelongsToFile(Index, filename.string().c_str(), &ErrInfo))
Index = nullptr;
// index valid but track number still not set?
@ -143,16 +137,13 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
if (TrackNumber == FFMS_TRACKMASK_ALL)
TrackNumber = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, &ErrInfo);
}
else {
else
FFMS_CancelIndexing(Indexer);
}
// update access time of index file so it won't get cleaned away
if (!wxFileName(CacheName).Touch()) {
// warn user?
}
agi::fs::Touch(CacheName);
AudioSource = FFMS_CreateAudioSource(FileNameShort.utf8_str(), TrackNumber, Index, -1, &ErrInfo);
AudioSource = FFMS_CreateAudioSource(filename.string().c_str(), TrackNumber, Index, -1, &ErrInfo);
if (!AudioSource)
throw agi::AudioProviderOpenError(std::string("Failed to open audio track: ") + ErrInfo.Buffer, 0);
@ -192,8 +183,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
}
void FFmpegSourceAudioProvider::FillBuffer(void *Buf, int64_t Start, int64_t Count) const {
if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo)) {
if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo))
throw AudioDecodeError(std::string("Failed to get audio samples: ") + ErrInfo.Buffer);
}
}
#endif /* WITH_FFMS2 */

View File

@ -34,8 +34,8 @@
#ifdef WITH_FFMS2
#include "include/aegisub/audio_provider.h"
#include "ffmpegsource_common.h"
#include "ffmpegsource_common.h"
/// @class FFmpegSourceAudioProvider
/// @brief Implements audio loading with the FFMS library.
@ -46,11 +46,11 @@ class FFmpegSourceAudioProvider : public AudioProvider, FFmpegSourceProvider {
mutable char FFMSErrMsg[1024]; ///< FFMS error message
mutable FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
void LoadAudio(wxString filename);
void LoadAudio(agi::fs::path const& filename);
void FillBuffer(void *buf, int64_t start, int64_t count) const;
public:
FFmpegSourceAudioProvider(wxString filename);
FFmpegSourceAudioProvider(agi::fs::path const& filename);
/// @brief Checks sample endianness
/// @return Returns true.

View File

@ -36,50 +36,43 @@
#include "audio_provider_hd.h"
#include <wx/filefn.h>
#include <wx/filename.h>
#include <libaegisub/background_runner.h>
#include <libaegisub/io.h>
#include "audio_controller.h"
#include "audio_provider_pcm.h"
#include "compat.h"
#include "options.h"
#include "standard_paths.h"
#include "utils.h"
namespace {
wxString cache_dir() {
wxString path = to_wx(OPT_GET("Audio/Cache/HD/Location")->GetString());
if (path == "default")
path = "?temp/";
#include <libaegisub/access.h>
#include <libaegisub/background_runner.h>
#include <libaegisub/fs.h>
#include <libaegisub/io.h>
#include <libaegisub/path.h>
#include <libaegisub/util.h>
return DecodeRelativePath(StandardPaths::DecodePath(path), StandardPaths::DecodePath("?user/"));
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
namespace {
agi::fs::path cache_dir() {
std::string path = OPT_GET("Audio/Cache/HD/Location")->GetString();
if (path == "default")
path = "?temp";
return config::path->MakeAbsolute(config::path->Decode(path), "?temp");
}
wxString cache_path() {
wxString pattern = to_wx(OPT_GET("Audio/Cache/HD/Name")->GetString());
if (pattern.Find("%02i") == wxNOT_FOUND) pattern = "audio%02i.tmp";
// Try from 00 to 99
for (int i=0;i<100;i++) {
// File exists?
wxFileName curNameTry(cache_dir(), wxString::Format(pattern, i));
#if wxCHECK_VERSION(2, 9, 4)
if (!curNameTry.Exists())
#else
if (!curNameTry.FileExists() && !curNameTry.DirExists())
#endif
return curNameTry.GetFullPath();
}
return "";
agi::fs::path cache_path() {
std::string pattern = OPT_GET("Audio/Cache/HD/Name")->GetString();
if (!boost::contains(pattern, "%02i")) pattern = "audio%02i.tmp";
boost::replace_all(pattern, "%02i", "%%%%-%%%%-%%%%-%%%%");
return unique_path(cache_dir()/pattern);
}
/// A PCM audio provider for raw dumps with no header
class RawAudioProvider : public PCMAudioProvider {
public:
RawAudioProvider(wxString const& cache_filename, AudioProvider *src)
RawAudioProvider(agi::fs::path const& cache_filename, AudioProvider *src)
: PCMAudioProvider(cache_filename)
{
bytes_per_sample = src->GetBytesPerSample();
@ -110,30 +103,27 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::BackgroundRunner *br)
float_samples = source->AreSamplesFloat();
// Check free space
wxDiskspaceSize_t freespace;
if (wxGetDiskSpace(cache_dir(), 0, &freespace)) {
if (num_samples * channels * bytes_per_sample > freespace)
throw agi::AudioCacheOpenError("Not enough free disk space in " + from_wx(cache_dir()) + " to cache the audio", 0);
}
if ((uint64_t)num_samples * channels * bytes_per_sample > agi::fs::FreeSpace(cache_dir()))
throw agi::AudioCacheOpenError("Not enough free disk space in " + cache_dir().string() + " to cache the audio", 0);
diskCacheFilename = cache_path();
try {
{
agi::io::Save out(from_wx(diskCacheFilename), true);
agi::io::Save out(diskCacheFilename, true);
br->Run(bind(&HDAudioProvider::FillCache, this, src, &out.Get(), std::placeholders::_1));
}
cache_provider.reset(new RawAudioProvider(diskCacheFilename, src));
}
catch (...) {
wxRemoveFile(diskCacheFilename);
agi::fs::Remove(diskCacheFilename);
throw;
}
}
HDAudioProvider::~HDAudioProvider() {
cache_provider.reset(); // explicitly close the file so we can delete it
wxRemoveFile(diskCacheFilename);
agi::fs::Remove(diskCacheFilename);
}
void HDAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {

View File

@ -32,8 +32,6 @@
/// @ingroup audio_input
///
#include <iosfwd>
#include "include/aegisub/audio_provider.h"
#include <libaegisub/scoped_ptr.h>
@ -45,7 +43,7 @@ namespace agi {
class HDAudioProvider : public AudioProvider {
/// Name of the file which the decoded audio is written to
wxString diskCacheFilename;
agi::fs::path diskCacheFilename;
/// Audio provider which reads from the decoded cache
agi::scoped_ptr<AudioProvider> cache_provider;

View File

@ -29,6 +29,6 @@ LockAudioProvider::LockAudioProvider(AudioProvider *source) : source(source) {
}
void LockAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
wxMutexLocker lock(mutex);
std::unique_lock<std::mutex> lock(mutex);
source->GetAudio(buf, start, count);
}

View File

@ -18,13 +18,12 @@
#include "include/aegisub/audio_provider.h"
#include <libaegisub/scoped_ptr.h>
#include <wx/thread.h>
#include <memory>
#include <mutex>
class LockAudioProvider : public AudioProvider {
agi::scoped_ptr<const AudioProvider> source;
mutable wxMutex mutex;
std::unique_ptr<const AudioProvider> source;
mutable std::mutex mutex;
void FillBuffer(void *buf, int64_t start, int64_t count) const;
public:

View File

@ -32,30 +32,26 @@
/// @ingroup audio_input
///
#include "config.h"
#include "audio_provider_pcm.h"
#include "aegisub_endian.h"
#include "audio_controller.h"
#include "utils.h"
#include <libaegisub/fs.h>
#include <libaegisub/log.h>
#include <cassert>
#include <cstdint>
#ifndef __WINDOWS__
#ifndef _WIN32
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#endif
#include <wx/file.h>
#include <wx/filename.h>
#include <wx/log.h>
#include <libaegisub/log.h>
#include "aegisub_endian.h"
#include "audio_controller.h"
#include "audio_provider_pcm.h"
#include "compat.h"
#include "utils.h"
PCMAudioProvider::PCMAudioProvider(const wxString &filename)
PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename)
: current_mapping(0)
, mapping_start(0)
, mapping_length(0)
@ -73,13 +69,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
0);
if (file_handle == INVALID_HANDLE_VALUE)
throw agi::FileNotFoundError(from_wx(filename));
LARGE_INTEGER li_file_size = {0};
if (!GetFileSizeEx(file_handle, &li_file_size))
throw agi::AudioProviderOpenError("Failed getting file size", 0);
file_size = li_file_size.QuadPart;
throw agi::fs::FileNotFound(filename);
file_mapping = CreateFileMapping(
file_handle,
@ -91,24 +81,22 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
if (file_mapping == 0)
throw agi::AudioProviderOpenError("Failed creating file mapping", 0);
#else
, file_handle(open(filename.mb_str(*wxConvFileName), O_RDONLY), close)
, file_handle(open(filename.c_str(), O_RDONLY), close)
{
if (file_handle == -1)
throw agi::FileNotFoundError(from_wx(filename));
struct stat filestats;
memset(&filestats, 0, sizeof(filestats));
if (fstat(file_handle, &filestats)) {
close(file_handle);
throw agi::AudioProviderOpenError("Could not stat file to get size", 0);
}
file_size = filestats.st_size;
throw agi::fs::FileNotFound(filename.string());
#endif
float_samples = false;
try {
file_size = agi::fs::Size(filename);
}
catch (agi::Exception const& e) {
throw agi::AudioPlayerOpenError("Could not get file size", e.Copy());
}
}
PCMAudioProvider::~PCMAudioProvider()
{
PCMAudioProvider::~PCMAudioProvider() {
#ifdef _WIN32
if (current_mapping)
UnmapViewOfFile(current_mapping);
@ -118,11 +106,9 @@ PCMAudioProvider::~PCMAudioProvider()
#endif
}
char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const
{
if (range_start + range_length > file_size) {
char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const {
if (range_start + range_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) {
@ -171,9 +157,8 @@ char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t rang
current_mapping = mmap(0, mapping_length, PROT_READ, MAP_PRIVATE, file_handle, mapping_start);
#endif
if (!current_mapping) {
if (!current_mapping)
throw AudioDecodeError("Failed mapping a view of the file");
}
}
assert(current_mapping);
@ -186,8 +171,7 @@ char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t rang
return ((char*)current_mapping) + rel_ofs;
}
void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const
{
void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
// Read blocks from the file
size_t index = 0;
while (count > 0 && index < index_points.size()) {
@ -268,10 +252,10 @@ class RiffWavPCMAudioProvider : public PCMAudioProvider {
public:
RiffWavPCMAudioProvider(const wxString &_filename)
: PCMAudioProvider(_filename)
RiffWavPCMAudioProvider(agi::fs::path const& filename)
: PCMAudioProvider(filename)
{
filename = _filename;
this->filename = filename;
// Read header
void *filestart = EnsureRangeAccessible(0, sizeof(RIFFChunk));
@ -345,8 +329,7 @@ public:
}
}
bool AreSamplesNativeEndian() const
{
bool AreSamplesNativeEndian() const {
// 8 bit samples don't consider endianness
if (bytes_per_sample < 2) return true;
// Otherwise test whether we're little endian
@ -411,17 +394,16 @@ class Wave64AudioProvider : public PCMAudioProvider {
uint64_t chunk_size;
};
inline bool CheckGuid(const uint8_t *guid1, const uint8_t *guid2)
{
bool CheckGuid(const uint8_t *guid1, const uint8_t *guid2) {
return memcmp(guid1, guid2, 16) == 0;
}
public:
Wave64AudioProvider(const wxString &_filename)
: PCMAudioProvider(_filename)
Wave64AudioProvider(agi::fs::path const& filename)
: PCMAudioProvider(filename)
{
filename = _filename;
this->filename = filename;
int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk);
@ -496,8 +478,7 @@ public:
}
}
bool AreSamplesNativeEndian() const
{
bool AreSamplesNativeEndian() const {
// 8 bit samples don't consider endianness
if (bytes_per_sample < 2) return true;
// Otherwise test whether we're little endian
@ -506,8 +487,7 @@ public:
}
};
AudioProvider *CreatePCMAudioProvider(const wxString &filename)
{
AudioProvider *CreatePCMAudioProvider(agi::fs::path const& filename) {
bool wrong_file_type = true;
std::string msg;

View File

@ -34,9 +34,6 @@
#include <vector>
#include <wx/file.h>
#include <wx/thread.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@ -63,7 +60,7 @@ class PCMAudioProvider : public AudioProvider {
#endif
protected:
PCMAudioProvider(const wxString &filename); // Create base object and open the file mapping
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
@ -84,4 +81,4 @@ protected:
};
// Construct the right PCM audio provider (if any) for the file
AudioProvider *CreatePCMAudioProvider(const wxString &filename);
AudioProvider *CreatePCMAudioProvider(agi::fs::path const& filename);

Some files were not shown because too many files have changed in this diff Show More