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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,18 +16,118 @@
/// @brief Character set detection and manipulation utilities. /// @brief Character set detection and manipulation utilities.
/// @ingroup libaegisub /// @ingroup libaegisub
#include "charset_ucd.h" #include "libaegisub/charset.h"
namespace agi { #include "libaegisub/io.h"
namespace charset {
std::string Detect(const std::string &file) { #include <fstream>
return UCDetect(file).Single(); #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) { namespace agi { namespace charset {
return UCDetect(file).List(); std::string Detect(agi::fs::path const& file) {
} return DetectAll(file).front().second;
}
} // namespace util CharsetListDetected DetectAll(agi::fs::path const& file) {
} // namespace agi 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 "../config.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <tuple>
#include "libaegisub/hotkey.h" #include "libaegisub/hotkey.h"
#include "libaegisub/cajun/writer.h" #include "libaegisub/cajun/writer.h"
@ -31,8 +26,12 @@
#include "libaegisub/json.h" #include "libaegisub/json.h"
#include "libaegisub/log.h" #include "libaegisub/log.h"
#include <algorithm>
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/map.hpp> #include <boost/range/adaptor/map.hpp>
#include <cmath>
#include <fstream>
#include <tuple>
namespace agi { namespace agi {
namespace hotkey { namespace hotkey {
@ -50,12 +49,12 @@ void Hotkey::ComboInsert(Combo const& combo) {
cmd_map.insert(make_pair(combo.CmdName(), 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) : config_file(file)
{ {
LOG_D("hotkey/init") << "Generating hotkeys."; 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) for (auto const& hotkey_context : object)
BuildHotkey(hotkey_context.first, hotkey_context.second); 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) { for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) {
std::string ctext = it->second.Context(); std::string ctext = it->second.Context();
if (ctext == "Always" || ctext == "Default" || ctext == 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()); sort(ret.begin(), ret.end());

View File

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

View File

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

View File

@ -17,16 +17,15 @@
/// @ingroup libaegisub /// @ingroup libaegisub
/// ///
#include "../config.h" #include "../config.h"
#include "libaegisub/keyframe.h"
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include "libaegisub/io.h" #include "libaegisub/io.h"
#include "libaegisub/line_iterator.h" #include "libaegisub/line_iterator.h"
#include "libaegisub/keyframe.h"
#include "libaegisub/vfr.h"
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/range/algorithm.hpp> #include <boost/range/algorithm.hpp>
@ -77,7 +76,7 @@ char x264(std::string const& line) {
} }
namespace agi { namespace keyframe { 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); io::Save file(filename);
std::ofstream& of = file.Get(); std::ofstream& of = file.Get();
of << "# keyframe format v1" << std::endl; 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")); 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::unique_ptr<std::ifstream> file(io::Open(filename));
std::istream &is(*file); std::istream &is(*file);

View File

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

View File

@ -30,7 +30,7 @@
namespace agi { 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) : config_name(config)
, options(options) , options(options)
{ {
@ -60,16 +60,22 @@ MRUManager::MRUListMap &MRUManager::Find(std::string const& key) {
return index->second; 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); MRUListMap &map = Find(key);
map.remove(entry); auto it = find(begin(map), end(map), entry);
map.push_front(entry); if (it == begin(map) && it != end(map))
Prune(key, map); return;
if (it != end(map))
map.splice(begin(map), map, it);
else {
map.push_front(entry);
Prune(key, map);
}
Flush(); 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); Find(key).remove(entry);
Flush(); Flush();
@ -79,7 +85,7 @@ const MRUManager::MRUListMap* MRUManager::Get(std::string const& key) {
return &Find(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); const MRUManager::MRUListMap *const map = Get(key);
if (entry >= map->size()) if (entry >= map->size())
@ -93,7 +99,8 @@ void MRUManager::Flush() {
for (auto const& mru_map : mru) { for (auto const& mru_map : mru) {
json::Array &array = out[mru_map.first]; 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()); 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. /// @param array json::Array of values.
void MRUManager::Load(std::string const& key, const json::Array& array) { void MRUManager::Load(std::string const& key, const json::Array& array) {
try { 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&) { catch (json::Exception const&) {
// Out of date MRU file; just discard the data and skip it // Out of date MRU file; just discard the data and skip it

View File

@ -20,14 +20,11 @@
#include "libaegisub/option.h" #include "libaegisub/option.h"
#include <cassert>
#include <memory>
#include <sstream>
#include "libaegisub/cajun/reader.h" #include "libaegisub/cajun/reader.h"
#include "libaegisub/cajun/writer.h" #include "libaegisub/cajun/writer.h"
#include "libaegisub/cajun/elements.h" #include "libaegisub/cajun/elements.h"
#include "libaegisub/fs.h"
#include "libaegisub/io.h" #include "libaegisub/io.h"
#include "libaegisub/log.h" #include "libaegisub/log.h"
#include "libaegisub/option_value.h" #include "libaegisub/option_value.h"
@ -35,6 +32,9 @@
#include "option_visit.h" #include "option_visit.h"
#include <boost/range/adaptor/map.hpp> #include <boost/range/adaptor/map.hpp>
#include <cassert>
#include <memory>
#include <sstream>
namespace { namespace {
/// @brief Write an option to a json object /// @brief Write an option to a json object
@ -66,7 +66,7 @@ namespace {
namespace agi { 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) : config_file(file)
, setting(setting) , setting(setting)
{ {
@ -88,21 +88,17 @@ void Options::ConfigNext(std::istream& stream) {
} }
void Options::ConfigUser() { void Options::ConfigUser() {
std::unique_ptr<std::istream> stream;
try { try {
stream.reset(agi::io::Open(config_file)); std::unique_ptr<std::istream> stream(io::Open(config_file));
} catch (const FileNotFoundError&) { LoadConfig(*stream, true);
}
catch (fs::FileNotFound const&) {
return; return;
} }
/// @todo Handle other errors such as parsing and notifying the user. /// @todo Handle other errors such as parsing and notifying the user.
LoadConfig(*stream, true);
} }
void Options::LoadConfig(std::istream& stream, bool ignore_errors) { 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; json::UnknownElement config_root;
try { 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/operator/comparison.hpp>
#include <boost/phoenix/core/argument.hpp> #include <boost/phoenix/core/argument.hpp>
#include <cstdlib> #include <fstream>
using boost::phoenix::placeholders::_1; using boost::phoenix::placeholders::_1;
namespace agi { 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)) : 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; std::string encoding_name;
getline(*idx, 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 // Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above // 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 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file util.cpp #include "../config.h"
/// @brief Unix utility methods.
/// @ingroup libaegisub
#include <errno.h>
#include <climits>
#include <cstdio>
#include "libaegisub/util.h" #include "libaegisub/util.h"
#include <boost/algorithm/string/case_conv.hpp> #include <ctime>
namespace agi { namespace agi { namespace util {
namespace util {
void str_lower(std::string &str) { std::string strftime(const char *fmt, const tm *tmptr) {
boost::to_lower(str); 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 /// @brief Framerate handling of all sorts
/// @ingroup libaegisub video_input /// @ingroup libaegisub video_input
#include "../config.h"
#include "libaegisub/vfr.h" #include "libaegisub/vfr.h"
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <fstream>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <list> #include <list>
@ -199,7 +202,7 @@ Framerate &Framerate::operator=(double fps) {
return *this = Framerate(fps); return *this = Framerate(fps);
} }
Framerate::Framerate(std::string const& filename) Framerate::Framerate(fs::path const& filename)
: denominator(default_denominator) : denominator(default_denominator)
, numerator(0) , numerator(0)
{ {
@ -221,7 +224,7 @@ Framerate::Framerate(std::string const& filename)
throw UnknownFormat(line); 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); agi::io::Save file(filename);
std::ofstream &out = file.Get(); std::ofstream &out = file.Get();

View File

@ -17,17 +17,11 @@
/// @ingroup libaegisub /// @ingroup libaegisub
#include <libaegisub/exception.h> #include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
namespace agi { namespace agi {
namespace acs { 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 { enum Type {
FileRead, FileRead,
DirRead, DirRead,
@ -35,13 +29,13 @@ enum Type {
DirWrite DirWrite
}; };
void Check(const std::string &file, acs::Type); void Check(fs::path const& file, acs::Type);
void CheckFileRead(const std::string &file); inline void CheckFileRead(fs::path const& file) { Check(file, acs::FileRead); }
void CheckDirRead(const std::string &dir); inline void CheckFileWrite(fs::path const& file) { Check(file, acs::FileWrite); }
void CheckFileWrite(const std::string &file); inline void CheckDirRead(fs::path const& dir) { Check(dir, acs::DirRead); }
void CheckDirWrite(const std::string &dir); inline void CheckDirWrite(fs::path const& dir) { Check(dir, acs::DirWrite); }
} // namespace axs } // namespace axs
} // namespace agi } // namespace agi

View File

@ -16,11 +16,11 @@
/// @brief Character set detection and manipulation utilities. /// @brief Character set detection and manipulation utilities.
/// @ingroup libaegisub /// @ingroup libaegisub
#include <fstream> #include <libaegisub/exception.h>
#include <functional> #include <libaegisub/fs_fwd.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <libaegisub/exception.h>
namespace agi { namespace agi {
/// Character set conversion and detection. /// 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. /// @brief Return a complete list of detected character sets ordered by precedence.
/// @param file File to check /// @param file File to check
/// @return List of possible charsets sorted by probability /// @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 /// @brief Returns the character set with the highest confidence
/// @param file File to check /// @param file File to check
/// @return Detected character set. /// @return Detected character set.
std::string Detect(const std::string &file); std::string Detect(agi::fs::path const& file);
} // namespace util } // namespace util
} // namespace agi } // 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 /// Convert a UTF-8 string to a string suitable for use with Win32 API functions
std::wstring ConvertW(std::string const& src); std::wstring ConvertW(std::string const& src);
std::string ConvertW(std::wstring 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 <memory>
#include <string> #include <string>
/// @see aegisub.h
namespace agi { namespace agi {
/// @class Exception /// @class Exception
/// @brief Base class for all exceptions in Aegisub. /// @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" /// could not be opened for reading". This is the purpose of the "inner"
/// exceptions. /// exceptions.
class Exception { class Exception {
/// The error message /// The error message
std::string message; std::string message;
@ -98,16 +94,13 @@ namespace agi {
std::shared_ptr<Exception> inner; std::shared_ptr<Exception> inner;
protected: protected:
/// @brief Protected constructor initialising members /// @brief Protected constructor initialising members
/// @param msg The error message /// @param msg The error message
/// @param inr The inner exception, optional /// @param inr The inner exception, optional
/// ///
/// Deriving classes should always use this constructor for initialising /// Deriving classes should always use this constructor for initialising
/// the base class. /// the base class.
Exception(const std::string &msg, const Exception *inr = 0) Exception(const std::string &msg, const Exception *inr = 0) : message(msg) {
: message(msg)
{
if (inr) if (inr)
inner.reset(inr->Copy()); inner.reset(inr->Copy());
} }
@ -120,9 +113,7 @@ namespace agi {
public: public:
/// @brief Destructor /// @brief Destructor
virtual ~Exception() virtual ~Exception() { }
{
}
/// @brief Get the outer exception error message /// @brief Get the outer exception error message
/// @return Error message /// @return Error message
@ -147,7 +138,6 @@ namespace agi {
/// level are [a-z0-9_]. /// level are [a-z0-9_].
virtual const char * GetName() const = 0; virtual const char * GetName() const = 0;
/// @brief Convert to char array as the error message /// @brief Convert to char array as the error message
/// @return The error message /// @return The error message
operator const char * () { return GetMessage().c_str(); } operator const char * () { return GetMessage().c_str(); }
@ -164,16 +154,12 @@ namespace agi {
virtual Exception *Copy() const = 0; virtual Exception *Copy() const = 0;
}; };
/// @brief Convenience macro to include the current location in code /// @brief Convenience macro to include the current location in code
/// ///
/// Intended for use in error messages where it can sometimes be convenient to /// Intended for use in error messages where it can sometimes be convenient to
/// indicate the exact position the error occurred at. /// indicate the exact position the error occurred at.
#define AG_WHERE " (at " __FILE__ ":" #__LINE__ ")" #define AG_WHERE " (at " __FILE__ ":" #__LINE__ ")"
/// @brief Convenience macro for declaring exceptions with no support for inner exception /// @brief Convenience macro for declaring exceptions with no support for inner exception
/// @param classname Name of the exception class to declare /// @param classname Name of the exception class to declare
/// @param baseclass Class to derive from /// @param baseclass Class to derive from
@ -231,7 +217,6 @@ namespace agi {
classname(const std::string &msg, Exception *inner) : baseclass(msg, inner) { } \ classname(const std::string &msg, Exception *inner) : baseclass(msg, inner) { } \
}; };
/// @class agi::UserCancelException /// @class agi::UserCancelException
/// @extends agi::Exception /// @extends agi::Exception
/// @brief Exception for "user cancel" events /// @brief Exception for "user cancel" events
@ -246,7 +231,6 @@ namespace agi {
/// code in question. /// code in question.
DEFINE_SIMPLE_EXCEPTION_NOINNER(UserCancelException,Exception,"nonerror/user_cancel") DEFINE_SIMPLE_EXCEPTION_NOINNER(UserCancelException,Exception,"nonerror/user_cancel")
/// @class agi::InternalError /// @class agi::InternalError
/// @extends agi:Exception /// @extends agi:Exception
/// @brief Errors that should never happen and point to some invalid assumption in the code /// @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(). /// and eventually cause an abort().
DEFINE_SIMPLE_EXCEPTION(InternalError, Exception, "internal_error") DEFINE_SIMPLE_EXCEPTION(InternalError, Exception, "internal_error")
/// @class agi::EnvironmentError
/// @class agi::FileSystemError /// @extends agi:Exception
/// @extends agi::Exception /// @brief The execution environment is broken in some fundamental way
/// @brief Base class for errors related to the file system
/// ///
/// This base class can not be instantiated. /// Throw an environment error when a call to the platform API has failed
/// File system errors do not support inner exceptions, as they are always originating /// in some way that should normally never happen or suggests that the
/// causes for errors. /// runtime environment is too insane to support.
DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError,Exception) DEFINE_SIMPLE_EXCEPTION_NOINNER(EnvironmentError, Exception, "environment_error")
/// @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); }
};
/// @class agi::InvalidInputException /// @class agi::InvalidInputException
/// @extends agi::Exception /// @extends agi::Exception
/// @brief Some input data were invalid and could not be processed /// @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 /// @brief Hotkey handler
/// @ingroup hotkey menu event window /// @ingroup hotkey menu event window
#include <boost/filesystem/path.hpp>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <libaegisub/fs_fwd.h>
#include <libaegisub/signal.h> #include <libaegisub/signal.h>
namespace json { namespace json {
@ -75,9 +77,9 @@ public:
/// Map to hold Combo instances /// Map to hold Combo instances
typedef std::multimap<std::string, Combo> HotkeyMap; typedef std::multimap<std::string, Combo> HotkeyMap;
private: private:
HotkeyMap str_map; ///< String representation -> Combo HotkeyMap str_map; ///< String representation -> Combo
HotkeyMap cmd_map; ///< Command name -> Combo HotkeyMap cmd_map; ///< Command name -> Combo
const std::string config_file; ///< Default user config location. const agi::fs::path config_file; ///< Default user config location.
/// Build hotkey map. /// Build hotkey map.
/// @param context Context being parsed. /// @param context Context being parsed.
@ -97,7 +99,7 @@ public:
/// Constructor /// Constructor
/// @param file Location of user config file. /// @param file Location of user config file.
/// @param default_config Default config. /// @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. /// Scan for a matching key.
/// @param context Context requested. /// @param context Context requested.

View File

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

View File

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

View File

@ -20,6 +20,7 @@
#include <vector> #include <vector>
#include "exception.h" #include "exception.h"
#include "fs_fwd.h"
namespace agi { namespace agi {
namespace vfr { class Framerate; } namespace vfr { class Framerate; }
@ -27,11 +28,12 @@ namespace agi {
/// @brief Load a keyframe file /// @brief Load a keyframe file
/// @param filename File to load /// @param filename File to load
/// @return List of frame numbers which are keyframes /// @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 /// @brief Save keyframes to a file
/// @param filename File to save to /// @param filename File to save to
/// @param keyframes List of keyframes to save /// @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") 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> template<class OutputType>
void line_iterator<OutputType>::getline(std::string &str) { void line_iterator<OutputType>::getline(std::string &str) {
union { union {

View File

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

View File

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

View File

@ -18,10 +18,12 @@
#pragma once #pragma once
#include <fstream> #include <boost/filesystem/path.hpp>
#include <iosfwd>
#include <map> #include <map>
#include <libaegisub/exception.h> #include <libaegisub/exception.h>
#include <libaegisub/fs_fwd.h>
namespace json { namespace json {
class UnknownElement; class UnknownElement;
@ -60,7 +62,7 @@ private:
OptionValueMap values; OptionValueMap values;
/// User config (file that will be written to disk) /// User config (file that will be written to disk)
const std::string config_file; const agi::fs::path config_file;
/// Settings. /// Settings.
const OptionSetting setting; const OptionSetting setting;
@ -74,7 +76,7 @@ public:
/// @brief Constructor /// @brief Constructor
/// @param file User config that will be loaded from and written back to. /// @param file User config that will be loaded from and written back to.
/// @param default_config Default configuration. /// @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 /// Destructor
~Options(); ~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 /// @brief MyThes-compatible thesaurus implementation
/// @ingroup libaegisub thesaurus /// @ingroup libaegisub thesaurus
#include <libaegisub/scoped_ptr.h> #include "fs_fwd.h"
#include <iosfwd> #include <iosfwd>
#include <map> #include <map>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
@ -31,9 +32,9 @@ class Thesaurus {
/// Map of word -> byte position in the data file /// Map of word -> byte position in the data file
std::map<std::string, int> offsets; std::map<std::string, int> offsets;
/// Read handle to the data file /// 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 /// Converter from the data file's charset to UTF-8
scoped_ptr<charset::IconvWrapper> conv; std::unique_ptr<charset::IconvWrapper> conv;
public: public:
/// A pair of a word and synonyms for that word /// A pair of a word and synonyms for that word
@ -42,7 +43,7 @@ public:
/// Constructor /// Constructor
/// @param dat_path Path to data file /// @param dat_path Path to data file
/// @param idx_path Path to index 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(); ~Thesaurus();
/// Look up synonyms for a word /// Look up synonyms for a word

View File

@ -16,57 +16,31 @@
/// @brief Public interface for general utilities. /// @brief Public interface for general utilities.
/// @ingroup libaegisub /// @ingroup libaegisub
#include <cstdint>
#include <string>
#include <algorithm> #include <algorithm>
#include <cstdint>
#include <string>
#include <libaegisub/types.h> #include <libaegisub/types.h>
struct tm;
namespace agi { namespace agi {
namespace util { namespace util {
/// Whether the path is a file or directory.
enum PathType {
TypeFile, ///< File
TypeDir ///< Directory
};
/// Clamp `b` to the range [`a`,`c`] /// 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)); } 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. /// Get time suitable for logging mechanisms.
/// @param tv timeval /// @param tv timeval
void time_log(agi_timeval &tv); 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, double *out);
bool try_parse(std::string const& str, int *out); bool try_parse(std::string const& str, int *out);
/// Check for amount of free space on a Path. /// strftime, but on std::string rather than a fixed buffer
/// @param path[in] Path to check /// @param fmt strftime format string
/// @param type PathType (default is TypeDir) /// @param tmptr Time to format, or nullptr for current time
uint64_t freespace(std::string const& path, PathType type=TypeDir); /// @return The strftime-formatted string
std::string strftime(const char *fmt, const tm *tmptr = nullptr);
struct delete_ptr { struct delete_ptr {
template<class T> template<class T>

View File

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

View File

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

View File

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

View File

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

View File

@ -20,59 +20,30 @@
#include "libaegisub/access.h" #include "libaegisub/access.h"
#include "libaegisub/fs.h"
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <iostream>
#include <fstream>
#include <unistd.h> #include <unistd.h>
#include "libaegisub/util.h" #include <boost/filesystem/path.hpp>
namespace agi { namespace agi {
namespace acs { namespace acs {
void Check(agi::fs::path const& file, acs::Type type) {
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) {
struct stat file_stat; 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) { if (file_status != 0) {
switch (errno) { switch (errno) {
case ENOENT: case ENOENT:
throw FileNotFoundError("File or path not found."); throw fs::FileNotFound(file);
break;
case EACCES: case EACCES:
throw Read("Access Denied to file, path or path component."); throw fs::ReadDenied(file);
break;
case EIO: case EIO:
throw Fatal("Fatal I/O error occurred."); throw fs::FileSystemUnknownError("Fatal I/O error in 'stat' on path: " + file.string());
break;
} }
} }
@ -80,24 +51,24 @@ void Check(const std::string &file, acs::Type type) {
case FileRead: case FileRead:
case FileWrite: case FileWrite:
if ((file_stat.st_mode & S_IFREG) == 0) if ((file_stat.st_mode & S_IFREG) == 0)
throw NotAFile("Not a file."); throw fs::NotAFile(file);
break; break;
case DirRead: case DirRead:
case DirWrite: case DirWrite:
if ((file_stat.st_mode & S_IFDIR) == 0) if ((file_stat.st_mode & S_IFDIR) == 0)
throw NotADirectory("Not a directory."); throw fs::NotADirectory(file);
break; break;
} }
file_status = access(file.c_str(), R_OK); file_status = access(file.c_str(), R_OK);
if (file_status != 0) if (file_status != 0)
throw Read("File or directory is not readable."); throw fs::ReadDenied(file);
if (type == DirWrite || type == FileWrite) { if (type == DirWrite || type == FileWrite) {
file_status = access(file.c_str(), W_OK); file_status = access(file.c_str(), W_OK);
if (file_status != 0) 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->file,
sm->func, sm->func,
sm->line, sm->line,
(int)sm->len, (int)sm->message.size(),
sm->message); sm->message.c_str());
if (!isatty(fileno(stdout))) if (!isatty(fileno(stdout)))
fflush(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/util.h"
#include "libaegisub/access.h" namespace agi { namespace util {
#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);
}
void time_log(timeval &tv) { void time_log(timeval &tv) {
gettimeofday(&tv, (struct timezone *)NULL); 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. /// @brief Windows access methods.
/// @ingroup libaegisub windows /// @ingroup libaegisub windows
#include <windows.h>
#include <iostream>
#include <fstream>
#include <libaegisub/access.h> #include <libaegisub/access.h>
#include <libaegisub/charset_conv_win.h> #include <libaegisub/fs.h>
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include <libaegisub/util.h> #include <libaegisub/util.h>
#include <libaegisub/util_win.h> #include <libaegisub/util_win.h>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <windows.h>
namespace { namespace {
bool check_permission(bool is_read, SECURITY_DESCRIPTOR *sd, HANDLE client_token) { 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; DWORD access_check = is_read ? FILE_READ_DATA : FILE_APPEND_DATA | FILE_WRITE_DATA;
@ -48,43 +49,23 @@ namespace {
namespace agi { namespace agi {
namespace acs { 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 This function is still a proof of concept, it's probably rife with bugs, below
is a short (and incomplete) todo is a short (and incomplete) todo
* "Basic" checks (Read/Write/File/Dir) checks for FAT32 filesystems which * "Basic" checks (Read/Write/File/Dir) checks for FAT32 filesystems which
requires detecting the filesystem being used. requires detecting the filesystem being used.
*/ */
void Check(const std::string &file, acs::Type type) { void Check(fs::path const& file, acs::Type type) {
std::wstring wfile = agi::charset::ConvertW(file); DWORD file_attr = GetFileAttributes(file.c_str());
DWORD file_attr = GetFileAttributes(wfile.c_str());
if ((file_attr & INVALID_FILE_ATTRIBUTES) == INVALID_FILE_ATTRIBUTES) { if ((file_attr & INVALID_FILE_ATTRIBUTES) == INVALID_FILE_ATTRIBUTES) {
switch (GetLastError()) { switch (GetLastError()) {
case ERROR_FILE_NOT_FOUND: case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND: case ERROR_PATH_NOT_FOUND:
throw FileNotFoundError(file); throw fs::FileNotFound(file);
case ERROR_ACCESS_DENIED: case ERROR_ACCESS_DENIED:
throw Read("Access denied to file or path component"); throw fs::ReadDenied(file);
default: 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 FileRead:
case FileWrite: case FileWrite:
if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
throw NotAFile(file + " is not a file"); throw fs::NotAFile(file);
break; break;
case DirRead: case DirRead:
case DirWrite: case DirWrite:
if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
throw NotADirectory(file + " is not a directory"); throw fs::NotADirectory(file);
break; break;
} }
SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
DWORD len = 0; DWORD len = 0;
GetFileSecurity(wfile.c_str(), info, nullptr, 0, &len); GetFileSecurity(file.c_str(), info, nullptr, 0, &len);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
LOG_W("acs/check") << "GetFileSecurity: fatal: " << util::ErrorString(GetLastError()); LOG_W("acs/check") << "GetFileSecurity: fatal: " << util::ErrorString(GetLastError());
std::vector<uint8_t> sd_buff(len); std::vector<uint8_t> sd_buff(len);
SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)&sd_buff[0]; 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()); LOG_W("acs/check") << "GetFileSecurity failed: " << util::ErrorString(GetLastError());
ImpersonateSelf(SecurityImpersonation); ImpersonateSelf(SecurityImpersonation);
@ -119,9 +100,9 @@ void Check(const std::string &file, acs::Type type) {
LOG_W("acs/check") << "OpenThreadToken failed: " << util::ErrorString(GetLastError()); LOG_W("acs/check") << "OpenThreadToken failed: " << util::ErrorString(GetLastError());
if (!check_permission(true, sd, client_token)) 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)) 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 } // namespace Access

View File

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

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. /// @brief Windows utility methods.
/// @ingroup libaegisub windows /// @ingroup libaegisub windows
#include <stdarg.h> #include "libaegisub/util_win.h"
#include <stdio.h>
#include <string> #include <string>
#include <fstream>
#include <windows.h>
#include "libaegisub/access.h"
#include "libaegisub/charset_conv_win.h" #include "libaegisub/charset_conv_win.h"
#include "libaegisub/types.h" #include "libaegisub/types.h"
#include "libaegisub/util.h"
#include "libaegisub/util_win.h"
namespace agi { namespace agi {
namespace util { namespace util {
using agi::charset::ConvertW; 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) { std::string ErrorString(DWORD error) {
LPWSTR lpstr = nullptr; LPWSTR lpstr = nullptr;
@ -116,19 +79,5 @@ void time_log(agi_timeval &tv) {
tv.tv_usec = (long)(tmpres % 1000000UL); 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 io
} // namespace agi } // namespace agi

View File

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

View File

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

View File

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

View File

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

View File

@ -32,21 +32,23 @@
/// @ingroup subs_storage /// @ingroup subs_storage
/// ///
#include "ass_entry.h"
#include <libaegisub/fs_fwd.h>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "ass_entry.h"
/// @class AssAttachment /// @class AssAttachment
class AssAttachment : public AssEntry { class AssAttachment : public AssEntry {
/// Decoded file data /// Decoded file data
std::shared_ptr<std::vector<char>> data; std::shared_ptr<std::vector<char>> data;
/// Encoded data which has been read from the script but not yet decoded /// 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 /// Name of the attached file, with SSA font mangling if it is a ttf
wxString filename; std::string filename;
AssEntryGroup group; AssEntryGroup group;
@ -56,25 +58,22 @@ public:
/// Add a line of data (without newline) read from a subtitle file to the /// Add a line of data (without newline) read from a subtitle file to the
/// buffer waiting to be decoded /// 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 /// Decode all data passed with AddData
void Finish(); void Finish();
/// Extract the contents of this attachment to a file /// Extract the contents of this attachment to a file
/// @param filename Path to save the attachment to /// @param filename Path to save the attachment to
void Extract(wxString const& filename) const; void Extract(agi::fs::path const& filename) const;
/// Import the contents of a file as an attachment
/// @param filename Path to import
void Import(wxString const& filename);
/// Get the name of the attached file /// Get the name of the attached file
/// @param raw If false, remove the SSA filename mangling /// @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; } AssEntryGroup Group() const override { return group; }
AssEntry *Clone() const override; 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 "config.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "compat.h"
#include "subtitle_format.h" #include "subtitle_format.h"
#include "utils.h" #include "utils.h"
#include <libaegisub/of_type_adaptor.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/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp> #include <boost/spirit/include/karma_generate.hpp>
#include <boost/spirit/include/karma_int.hpp>
#include <wx/regex.h>
#include <wx/tokenzr.h>
using namespace boost::adaptors; using namespace boost::adaptors;
static int next_id = 0; static int next_id = 0;
std::size_t hash_value(wxString const& s) {
return wxStringHash()(s);
}
AssDialogue::AssDialogue() AssDialogue::AssDialogue()
: Id(++next_id) : Id(++next_id)
, Comment(false) , Comment(false)
@ -80,105 +77,91 @@ AssDialogue::AssDialogue(AssDialogue const& that)
memmove(Margin, that.Margin, sizeof Margin); memmove(Margin, that.Margin, sizeof Margin);
} }
AssDialogue::AssDialogue(wxString const& data) AssDialogue::AssDialogue(std::string const& data)
: Id(++next_id) : Id(++next_id)
{ {
if (!Parse(data)) Parse(data);
throw SubtitleFormatParseError(from_wx("Failed parsing line: " + data), 0);
} }
AssDialogue::~AssDialogue () { AssDialogue::~AssDialogue () {
} }
bool AssDialogue::Parse(wxString const& rawData) { class tokenizer {
size_t pos = 0; agi::StringRange str;
wxString temp; boost::split_iterator<agi::StringRange::const_iterator> pos;
// Get type public:
if (rawData.StartsWith("Dialogue:")) { 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; 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; 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); tokenizer tkn(str);
if (!tkn.HasMoreTokens()) return false;
// Get first token and see if it has "Marked=" in it // Get first token and see if it has "Marked=" in it
temp = tkn.GetNextToken().Trim(false).Trim(true); auto tmp = tkn.next_str_trim();
bool ssa = temp.Lower().StartsWith("marked="); bool ssa = boost::istarts_with(tmp, "marked=");
// Get layer number // Get layer number
if (ssa) if (ssa)
Layer = 0; Layer = 0;
else { else
long templ; Layer = boost::lexical_cast<int>(tmp);
temp.ToLong(&templ);
Layer = templ;
}
// Get start time Start = tkn.next_str_trim();
if (!tkn.HasMoreTokens()) return false; End = tkn.next_str_trim();
Start = tkn.GetNextToken(); Style = tkn.next_str_trim();
Actor = tkn.next_str_trim();
// Get end time for (int& margin : Margin)
if (!tkn.HasMoreTokens()) return false; margin = mid(0, boost::lexical_cast<int>(tkn.next_str()), 9999);
End = tkn.GetNextToken(); Effect = tkn.next_str_trim();
Text = std::string(tkn.next_tok().begin(), str.end());
// 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;
} }
static void append_int(wxString &str, int v) { void append_int(std::string &str, int v) {
str += std::to_wstring(v); boost::spirit::karma::generate(back_inserter(str), boost::spirit::karma::int_, v);
str += ','; str += ',';
} }
static void append_str(wxString &out, wxString const& str) { void append_str(std::string &out, std::string const& str) {
out += str; out += str;
out += ','; 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) if (str.find(',') == str.npos)
out += str; out += str;
else { else
wxString c = str; out += boost::replace_all_copy(str, ",", ";");
c.Replace(wxS(","), wxS(";"));
out += c;
}
out += ','; out += ',';
} }
wxString AssDialogue::GetData(bool ssa) const { std::string AssDialogue::GetData(bool ssa) const {
wxString str = Comment ? wxS("Comment: ") : wxS("Dialogue: "); std::string str = Comment ? "Comment: " : "Dialogue: ";
str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size()); str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size());
if (ssa) if (ssa)
append_str(str, wxS("Marked=0")); append_str(str, "Marked=0");
else else
append_int(str, Layer); append_int(str, Layer);
append_str(str, Start.GetAssFormated()); append_str(str, Start.GetAssFormated());
@ -190,20 +173,19 @@ wxString AssDialogue::GetData(bool ssa) const {
append_unsafe_str(str, Effect); append_unsafe_str(str, Effect);
str += Text.get(); str += Text.get();
// Make sure that final has no line breaks
if (str.find('\n') != str.npos || str.find('\r') != str.npos) { if (str.find('\n') != str.npos || str.find('\r') != str.npos) {
str.Replace("\n", ""); boost::replace_all(str, "\n", "");
str.Replace("\r", ""); boost::replace_all(str, "\r", "");
} }
return str; return str;
} }
const wxString AssDialogue::GetEntryData() const { const std::string AssDialogue::GetEntryData() const {
return GetData(false); return GetData(false);
} }
wxString AssDialogue::GetSSAText() const { std::string AssDialogue::GetSSAText() const {
return GetData(true); return GetData(true);
} }
@ -217,7 +199,7 @@ std::auto_ptr<boost::ptr_vector<AssDialogueBlock>> AssDialogue::ParseTags() cons
} }
int drawingLevel = 0; 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; ) { for (size_t len = text.size(), cur = 0; cur < len; ) {
// Overrides block // Overrides block
@ -284,32 +266,17 @@ void AssDialogue::StripTags() {
static std::string get_text(AssDialogueBlock &d) { return d.GetText(); } static std::string get_text(AssDialogueBlock &d) { return d.GetText(); }
void AssDialogue::UpdateText(boost::ptr_vector<AssDialogueBlock>& blocks) { void AssDialogue::UpdateText(boost::ptr_vector<AssDialogueBlock>& blocks) {
if (blocks.empty()) return; 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(); if (which < 0 || which > 2) throw InvalidMarginIdError();
Margin[which] = mid<int>(0, atoi(origvalue.c_str()), 9999);
// 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);
} }
wxString AssDialogue::GetMarginString(int which) const { std::string AssDialogue::GetMarginString(int which) const {
if (which < 0 || which > 2) throw InvalidMarginIdError(); 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 { 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(); } static std::string get_text_p(AssDialogueBlock *d) { return d->GetText(); }
wxString AssDialogue::GetStrippedText() const { std::string AssDialogue::GetStrippedText() const {
wxString ret;
boost::ptr_vector<AssDialogueBlock> blocks(ParseTags()); 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 { AssEntry *AssDialogue::Clone() const {
@ -336,10 +302,9 @@ void AssDialogueBlockDrawing::TransformCoords(int mx, int my, double x, double y
bool is_x = true; bool is_x = true;
std::string final; std::string final;
boost::char_separator<char> sep(" "); for (auto const& cur : agi::Split(text, ' ')) {
for (auto const& cur : boost::tokenizer<boost::char_separator<char>>(text, sep)) {
if (std::all_of(begin(cur), end(cur), isdigit)) { 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) if (is_x)
val = (int)((val + mx) * x + .5); val = (int)((val + mx) * x + .5);
else else

View File

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

View File

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

View File

@ -34,9 +34,8 @@
#pragma once #pragma once
#include <wx/string.h>
#include <boost/intrusive/list_hook.hpp> #include <boost/intrusive/list_hook.hpp>
#include <string>
enum AssEntryGroup { enum AssEntryGroup {
ENTRY_INFO = 0, ENTRY_INFO = 0,
@ -58,11 +57,11 @@ public:
virtual AssEntryGroup Group() const=0; virtual AssEntryGroup Group() const=0;
/// ASS or SSA Section header for this entry's group /// 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 /// @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 /// 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 "config.h"
#include <algorithm>
#include "ass_export_filter.h" #include "ass_export_filter.h"
#include "utils.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) : name(name)
, priority(priority) , priority(priority)
, description(description) , description(description)
@ -47,52 +54,44 @@ AssExportFilter::AssExportFilter(wxString const& name, wxString const& descripti
} }
void AssExportFilterChain::Register(AssExportFilter *filter) { void AssExportFilterChain::Register(AssExportFilter *filter) {
// Remove pipes from name
filter->name.Replace("|", "");
int filter_copy = 1; int filter_copy = 1;
wxString name = filter->name; std::string name = filter->name;
// Find a unique name // Find a unique name
while (GetFilter(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; filter->name = name;
// Look for place to insert // Look for place to insert
FilterList::iterator begin = filters()->begin(); auto begin(filters().begin()), end(filters().end());
FilterList::iterator end = filters()->end();
while (begin != end && (*begin)->priority >= filter->priority) ++begin; while (begin != end && (*begin)->priority >= filter->priority) ++begin;
filters()->insert(begin, filter); filters().insert(begin, filter);
} }
void AssExportFilterChain::Unregister(AssExportFilter *filter) { void AssExportFilterChain::Unregister(AssExportFilter *filter) {
auto it = remove(begin(*filters()), end(*filters()), filter); auto it = remove(begin(filters()), end(filters()), filter);
if (it == end(*filters())) if (it == end(filters()))
throw wxString::Format("Unregister export filter: name \"%s\" is not registered.", filter->name); 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() { const FilterList *AssExportFilterChain::GetFilterList() {
return filters(); return &filters();
} }
void AssExportFilterChain::Clear() { void AssExportFilterChain::Clear() {
while (filters()->size() > 0) { while (filters().size() > 0) {
AssExportFilter *f = filters()->back(); AssExportFilter *f = filters().back();
delete f; delete f;
if (filters()->size() && filters()->back() == f) if (filters().size() && filters().back() == f)
filters()->pop_back(); filters().pop_back();
} }
} }
AssExportFilter *AssExportFilterChain::GetFilter(wxString const& name) { AssExportFilter *AssExportFilterChain::GetFilter(std::string const& name) {
for (auto filter : *filters()) { for (auto filter : filters()) {
if (filter->name == name) if (filter->name == name)
return filter; return filter;
} }

View File

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

View File

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

View File

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

View File

@ -35,21 +35,10 @@
#include "ass_file.h" #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_attachment.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_info.h" #include "ass_info.h"
#include "ass_style.h" #include "ass_style.h"
#include "compat.h"
#include "options.h" #include "options.h"
#include "standard_paths.h" #include "standard_paths.h"
#include "subtitle_format.h" #include "subtitle_format.h"
@ -57,7 +46,17 @@
#include "text_file_writer.h" #include "text_file_writer.h"
#include "utils.h" #include "utils.h"
#include <libaegisub/dispatch.h>
#include <libaegisub/fs.h>
#include <libaegisub/of_type_adaptor.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 { namespace std {
template<> template<>
@ -73,16 +72,18 @@ AssFile::AssFile ()
} }
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 /// @brief Load generic subs
void AssFile::Load(const wxString &_filename, wxString const& charset) { void AssFile::Load(agi::fs::path const& filename, std::string const& charset) {
const SubtitleFormat *reader = SubtitleFormat::GetReader(_filename); const SubtitleFormat *reader = SubtitleFormat::GetReader(filename);
try { try {
AssFile temp; AssFile temp;
reader->ReadFile(&temp, _filename, charset); reader->ReadFile(&temp, filename, charset);
bool found_style = false; bool found_style = false;
bool found_dialogue = false; bool found_dialogue = false;
@ -109,7 +110,7 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) {
// Set general data // Set general data
loaded = true; loaded = true;
filename = _filename; this->filename = filename;
// Add comments and set vars // Add comments and set vars
SetScriptInfo("ScriptType", "v4.00+"); SetScriptInfo("ScriptType", "v4.00+");
@ -124,7 +125,7 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) {
FileOpen(filename); 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); const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
if (!writer) if (!writer)
throw "Unknown file type."; throw "Unknown file type.";
@ -132,47 +133,38 @@ void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxStri
if (setfilename) { if (setfilename) {
autosavedCommitId = savedCommitId = commitId; autosavedCommitId = savedCommitId = commitId;
this->filename = filename; this->filename = filename;
StandardPaths::SetPathValue("?script", wxFileName(filename).GetPath()); StandardPaths::SetPathValue("?script", filename.parent_path());
} }
FileSave(); FileSave();
writer->WriteFile(this, filename, encoding); writer->WriteFile(this, filename, encoding);
if (addToRecent) { if (addToRecent)
AddToRecent(filename); AddToRecent(filename);
}
} }
wxString AssFile::AutoSave() { agi::fs::path AssFile::AutoSave() {
if (!loaded || commitId == autosavedCommitId) if (!loaded || commitId == autosavedCommitId)
return ""; return "";
wxFileName origfile(filename); auto path = StandardPaths::DecodePath(OPT_GET("Path/Auto/Save")->GetString());
wxString path = to_wx(OPT_GET("Path/Auto/Save")->GetString()); if (path.empty())
if (!path) path = filename.parent_path();
path = origfile.GetPath();
path = StandardPaths::DecodePath(path + "/");
wxFileName dstpath(path); agi::fs::CreateDirectory(path);
if (!dstpath.DirExists())
wxMkdir(path);
wxString name = origfile.GetName(); auto name = filename.filename();
if (!name) if (name.empty())
name = "Untitled"; 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; autosavedCommitId = commitId;
return dstpath.GetFullPath(); return path;
}
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));
} }
void AssFile::SaveMemory(std::vector<char> &dst) { void AssFile::SaveMemory(std::vector<char> &dst) {
@ -190,9 +182,9 @@ void AssFile::SaveMemory(std::vector<char> &dst) {
for (auto const& line : Line) { for (auto const& line : Line) {
if (group != line.Group()) { if (group != line.Group()) {
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) { void AssFile::LoadDefault(bool defline) {
Clear();
// Write headers
Line.push_back(*new AssInfo("Title", "Default Aegisub file")); Line.push_back(*new AssInfo("Title", "Default Aegisub file"));
Line.push_back(*new AssInfo("ScriptType", "v4.00+")); Line.push_back(*new AssInfo("ScriptType", "v4.00+"));
Line.push_back(*new AssInfo("WrapStyle", "0")); Line.push_back(*new AssInfo("WrapStyle", "0"));
Line.push_back(*new AssInfo("ScaledBorderAndShadow", "yes")); Line.push_back(*new AssInfo("ScaledBorderAndShadow", "yes"));
Line.push_back(*new AssInfo("Collisions", "Normal")); Line.push_back(*new AssInfo("Collisions", "Normal"));
if (!OPT_GET("Subtitle/Default Resolution/Auto")->GetBool()) { 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("PlayResX", std::to_string(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("PlayResY", std::to_string(OPT_GET("Subtitle/Default Resolution/Height")->GetInt())));
} }
Line.push_back(*new AssInfo("YCbCr Matrix", "None")); Line.push_back(*new AssInfo("YCbCr Matrix", "None"));
@ -283,40 +262,32 @@ void AssFile::InsertLine(AssEntry *entry) {
Line.push_front(*entry); Line.push_front(*entry);
} }
void AssFile::InsertAttachment(wxString filename) { void AssFile::InsertAttachment(agi::fs::path const& filename) {
AssEntryGroup group = ENTRY_GRAPHIC; 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") if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb")
group = ENTRY_FONT; group = ENTRY_FONT;
std::unique_ptr<AssAttachment> newAttach(new AssAttachment(wxFileName(filename).GetFullName(), group)); InsertLine(new AssAttachment(filename, group));
newAttach->Import(filename);
InsertLine(newAttach.release());
} }
wxString AssFile::GetScriptInfo(wxString key) const { std::string AssFile::GetScriptInfo(std::string const& key) const {
key.MakeLower();
for (const auto info : Line | agi::of_type<AssInfo>()) { for (const auto info : Line | agi::of_type<AssInfo>()) {
if (key == info->Key().Lower()) if (boost::iequals(key, info->Key()))
return info->Value(); return info->Value();
} }
return ""; return "";
} }
int AssFile::GetScriptInfoAsInt(wxString const& key) const { int AssFile::GetScriptInfoAsInt(std::string const& key) const {
long temp = 0; return atoi(GetScriptInfo(key).c_str());
GetScriptInfo(key).ToLong(&temp);
return temp;
} }
void AssFile::SetScriptInfo(wxString const& key, wxString const& value) { void AssFile::SetScriptInfo(std::string const& key, std::string const& value) {
wxString lower_key = key.Lower();
for (auto info : Line | agi::of_type<AssInfo>()) { for (auto info : Line | agi::of_type<AssInfo>()) {
if (lower_key == info->Key().Lower()) { if (boost::iequals(key, info->Key())) {
if (value.empty()) if (value.empty())
delete info; delete info;
else else
@ -366,10 +337,9 @@ AssStyle *AssFile::GetStyle(std::string const& name) {
return nullptr; return nullptr;
} }
void AssFile::AddToRecent(wxString const& file) const { void AssFile::AddToRecent(agi::fs::path const& file) const {
config::mru->Add("Subtitle", from_wx(file)); config::mru->Add("Subtitle", file);
wxFileName filepath(file); OPT_SET("Path/Last/Subtitles")->SetString(file.parent_path().string());
OPT_SET("Path/Last/Subtitles")->SetString(from_wx(filepath.GetPath()));
} }
int AssFile::Commit(wxString const& desc, int type, int amendId, AssEntry *single_line) { 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/container/list.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/intrusive/list.hpp> #include <boost/intrusive/list.hpp>
#include <set> #include <set>
#include <vector> #include <vector>
#include <wx/string.h>
#include <libaegisub/fs_fwd.h>
#include <libaegisub/signal.h> #include <libaegisub/signal.h>
#include "ass_entry.h" #include "ass_entry.h"
@ -63,7 +66,7 @@ class AssFile {
/// A set of changes has been committed to the file (AssFile::CommitType) /// A set of changes has been committed to the file (AssFile::CommitType)
agi::signal::Signal<int, std::set<const AssEntry*> const&> AnnounceCommit; agi::signal::Signal<int, std::set<const AssEntry*> const&> AnnounceCommit;
/// A new file has been opened (filename) /// 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 /// The file is about to be saved
/// This signal is intended for adding metadata such as video filename, /// This signal is intended for adding metadata such as video filename,
/// frame number, etc. Ideally this would all be done immediately rather /// frame number, etc. Ideally this would all be done immediately rather
@ -74,7 +77,7 @@ public:
/// The lines in the file /// The lines in the file
EntryList Line; EntryList Line;
/// The filename of this file, if any /// The filename of this file, if any
wxString filename; agi::fs::path filename;
/// Is the file loaded? /// Is the file loaded?
bool loaded; bool loaded;
@ -84,9 +87,7 @@ public:
~AssFile(); ~AssFile();
/// Does the file have unsaved changes? /// Does the file have unsaved changes?
bool IsModified() const {return commitId != savedCommitId; }; bool IsModified() const { return commitId != savedCommitId; };
/// Clear the file
void Clear();
/// @brief Load default file /// @brief Load default file
/// @param defline Add a blank line to the 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 /// Add a line to the file at the end of the appropriate section
void InsertLine(AssEntry *line); void InsertLine(AssEntry *line);
/// Attach a file to the ass file /// 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 /// Get the names of all of the styles available
std::vector<std::string> GetStyles() const; std::vector<std::string> GetStyles() const;
/// @brief Get a style by name /// @brief Get a style by name
@ -107,24 +108,24 @@ public:
/// @brief Load from a file /// @brief Load from a file
/// @param file File name /// @param file File name
/// @param charset Character set of file or empty to autodetect /// @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 /// @brief Save to a file
/// @param file Path to save to /// @param file Path to save to
/// @param setfilename Should the filename be changed to the passed path? /// @param setfilename Should the filename be changed to the passed path?
/// @param addToRecent Should the file be added to the MRU list? /// @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") /// @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 /// @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 /// @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 /// @brief Save to a memory buffer. Used for subtitle providers which support it
/// @param[out] dst Destination vector /// @param[out] dst Destination vector
void SaveMemory(std::vector<char> &dst); void SaveMemory(std::vector<char> &dst);
/// Add file name to the MRU list /// 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? /// Can the file be saved in its current format?
bool CanSave() const; bool CanSave() const;
@ -133,11 +134,11 @@ public:
/// @param[in] h Height /// @param[in] h Height
void GetResolution(int &w,int &h) const; void GetResolution(int &w,int &h) const;
/// Get the value in a [Script Info] key as int, or 0 if it is not present /// 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. /// 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. /// 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 /// Type of changes made in a commit
enum CommitType { enum CommitType {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,12 +36,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <wx/string.h>
class AssStyle; class AssStyle;
class AssStyleStorage { class AssStyleStorage {
wxString storage_name; std::string storage_name;
std::deque<AssStyle*> style; std::deque<AssStyle*> style;
public: public:
@ -77,5 +75,5 @@ public:
/// Load stored styles from a file /// Load stored styles from a file
/// @param name Catalog name (note: not file name) /// @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 // Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
@ -36,61 +23,64 @@
#include "ass_time.h" #include "ass_time.h"
#include <algorithm>
#include <cmath>
#include <wx/tokenzr.h>
#include "utils.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(int time) : time(mid(0, time, 10 * 60 * 60 * 1000 - 1)) { }
AssTime::AssTime(wxString const& text) AssTime::AssTime(std::string const& text)
: time(0) : time(0)
{ {
size_t pos = 0, end = 0; int after_decimal = -1;
int current = 0;
int colons = text.Freq(':'); for (char c : text | boost::adaptors::filtered(boost::is_any_of(":0123456789."))) {
if (c == ':') {
// Set start so that there are only two colons at most time = time * 60 + current;
for (; colons > 2; --colons) pos = text.find(':', pos) + 1; current = 0;
}
// Hours else if (c == '.') {
if (colons == 2) { time = (time * 60 + current) * 1000;
while (text[end++] != ':') { } current = 0;
time += AegiStringToInt(text, pos, end) * 60 * 60 * 1000; after_decimal = 100;
pos = end; }
else if (after_decimal < 0) {
current *= 10;
current += c - '0';
}
else {
time += (c - '0') * after_decimal;
after_decimal /= 10;
}
} }
// Minutes // Never saw a decimal, so convert now to ms
if (colons >= 1) { if (after_decimal < 0)
while (text[end++] != ':') { } time = (time * 60 + current) * 1000;
time += AegiStringToInt(text, pos, end) * 60 * 1000;
}
// Milliseconds (includes seconds)
time += AegiStringToFix(text, 3, end, text.size());
// Limit to the valid range // Limit to the valid range
time = mid(0, time, 10 * 60 * 60 * 1000 - 1); time = mid(0, time, 10 * 60 * 60 * 1000 - 1);
} }
wxString AssTime::GetAssFormated(bool msPrecision) const { std::string AssTime::GetAssFormated(bool msPrecision) const {
wxChar ret[] = { std::string ret(10 + msPrecision, ':');
'0' + GetTimeHours(), ret[0] = '0' + GetTimeHours();
':', ret[2] = '0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10);
'0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10), ret[3] = '0' + (time % (10 * 60 * 1000)) / (60 * 1000);
'0' + (time % (10 * 60 * 1000)) / (60 * 1000), ret[5] = '0' + (time % (60 * 1000)) / (1000 * 10);
':', ret[6] = '0' + (time % (10 * 1000)) / 1000;
'0' + (time % (60 * 1000)) / (1000 * 10), ret[7] = '.';
'0' + (time % (10 * 1000)) / 1000, ret[8] = '0' + (time % 1000) / 100;
'.', ret[9] = '0' + (time % 100) / 10;
'0' + (time % 1000) / 100, if (msPrecision)
'0' + (time % 100) / 10, ret[10] = '0' + time % 10;
'0' + time % 10 return ret;
};
return wxString(ret, msPrecision ? 11 : 10);
} }
int AssTime::GetTimeHours() const { return time / 3600000; } 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::GetTimeMiliseconds() const { return (time % 1000); }
int AssTime::GetTimeCentiseconds() const { return (time % 1000) / 10; } 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) : fps(fps)
, sep(sep) , sep(sep)
{ {
} }
wxString SmpteFormatter::ToSMPTE(AssTime time) const { std::string SmpteFormatter::ToSMPTE(AssTime time) const {
int h=0, m=0, s=0, f=0; int h=0, m=0, s=0, f=0;
fps.SmpteAtTime(time, &h, &m, &s, &f); 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 { AssTime SmpteFormatter::FromSMPTE(std::string const& str) const {
long h, m, s, f; std::vector<std::string> toks;
wxArrayString toks = wxStringTokenize(str, sep); boost::split(toks, str, boost::is_any_of(sep));
if (toks.size() != 4) return 0; if (toks.size() != 4) return 0;
toks[0].ToLong(&h);
toks[1].ToLong(&m); int h, m, s, f;
toks[2].ToLong(&s); agi::util::try_parse(toks[0], &h);
toks[3].ToLong(&f); 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); return fps.TimeAtSmpte(h, m, s, f);
} }

View File

@ -1,29 +1,16 @@
// Copyright (c) 2005, Rodrigo Braz Monteiro // Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
@ -34,7 +21,7 @@
#pragma once #pragma once
#include <wx/string.h> #include <string>
#include <libaegisub/vfr.h> #include <libaegisub/vfr.h>
@ -44,7 +31,7 @@ class AssTime {
public: public:
AssTime(int ms = 0); AssTime(int ms = 0);
AssTime(wxString const& text); AssTime(std::string const& text);
/// Get millisecond, rounded to centisecond precision /// Get millisecond, rounded to centisecond precision
operator int() const { return time / 10 * 10; } operator int() const { return time / 10 * 10; }
@ -57,7 +44,7 @@ public:
/// Return the time as a string /// Return the time as a string
/// @param ms Use milliseconds precision, for non-ASS formats /// @param ms Use milliseconds precision, for non-ASS formats
wxString GetAssFormated(bool ms=false) const; std::string GetAssFormated(bool ms=false) const;
}; };
/// @class SmpteFormatter /// @class SmpteFormatter
@ -66,13 +53,13 @@ class SmpteFormatter {
/// Frame rate to use /// Frame rate to use
agi::vfr::Framerate fps; agi::vfr::Framerate fps;
/// Separator character /// Separator character
char sep; std::string sep;
public: public:
SmpteFormatter(agi::vfr::Framerate fps, char sep=':'); SmpteFormatter(agi::vfr::Framerate fps, std::string const& sep=":");
/// Convert an AssTime to a SMPTE timecode /// 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 /// 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 "config.h"
#include <algorithm> #include "audio_controller.h"
#include <wx/filename.h>
#include <libaegisub/io.h>
#include "ass_file.h" #include "ass_file.h"
#include "audio_controller.h"
#include "audio_provider_dummy.h" #include "audio_provider_dummy.h"
#include "audio_timing.h" #include "audio_timing.h"
#include "compat.h" #include "compat.h"
@ -51,10 +46,14 @@
#include "pen.h" #include "pen.h"
#include "options.h" #include "options.h"
#include "selection_controller.h" #include "selection_controller.h"
#include "standard_paths.h"
#include "utils.h" #include "utils.h"
#include "video_context.h" #include "video_context.h"
#include <libaegisub/io.h>
#include <libaegisub/path.h>
#include <algorithm>
AudioController::AudioController(agi::Context *context) AudioController::AudioController(agi::Context *context)
: context(context) : context(context)
, subtitle_save_slot(context->ass->AddFileSaveListener(&AudioController::OnSubtitlesSave, this)) , subtitle_save_slot(context->ass->AddFileSaveListener(&AudioController::OnSubtitlesSave, this))
@ -81,13 +80,11 @@ AudioController::AudioController(agi::Context *context)
#endif #endif
} }
AudioController::~AudioController() AudioController::~AudioController()
{ {
CloseAudio(); CloseAudio();
} }
void AudioController::OnPlaybackTimer(wxTimerEvent &) void AudioController::OnPlaybackTimer(wxTimerEvent &)
{ {
int64_t pos = player->GetCurrentPosition(); int64_t pos = player->GetCurrentPosition();
@ -105,7 +102,6 @@ void AudioController::OnPlaybackTimer(wxTimerEvent &)
} }
} }
#ifdef wxHAS_POWER_EVENTS #ifdef wxHAS_POWER_EVENTS
void AudioController::OnComputerSuspending(wxPowerEvent &) void AudioController::OnComputerSuspending(wxPowerEvent &)
{ {
@ -114,7 +110,6 @@ void AudioController::OnComputerSuspending(wxPowerEvent &)
player = 0; player = 0;
} }
void AudioController::OnComputerResuming(wxPowerEvent &) void AudioController::OnComputerResuming(wxPowerEvent &)
{ {
if (provider) if (provider)
@ -154,25 +149,25 @@ void AudioController::OnAudioProviderChanged()
{ {
if (IsAudioOpen()) if (IsAudioOpen())
// url is cloned because CloseAudio clears it and OpenAudio takes a const reference // 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); throw agi::InternalError("AudioController::OpenAudio() was passed an empty string. This must not happen.", 0);
AudioProvider *new_provider = 0; AudioProvider *new_provider = 0;
try { try {
new_provider = AudioProviderFactory::GetProvider(url); new_provider = AudioProviderFactory::GetProvider(url);
StandardPaths::SetPathValue("?audio", wxFileName(url).GetPath()); config::path->SetToken("?audio", url);
} }
catch (agi::UserCancelException const&) { catch (agi::UserCancelException const&) {
throw; throw;
} }
catch (...) { catch (...) {
config::mru->Remove("Audio", from_wx(url)); config::mru->Remove("Audio", url);
throw; throw;
} }
@ -192,11 +187,10 @@ void AudioController::OpenAudio(const wxString &url)
audio_url = url; audio_url = url;
config::mru->Add("Audio", from_wx(url)); config::mru->Add("Audio", url);
try try
{ {
// Tell listeners about this.
AnnounceAudioOpen(provider); AnnounceAudioOpen(provider);
} }
catch (...) catch (...)
@ -206,7 +200,6 @@ void AudioController::OpenAudio(const wxString &url)
} }
} }
void AudioController::CloseAudio() void AudioController::CloseAudio()
{ {
Stop(); Stop();
@ -218,7 +211,7 @@ void AudioController::CloseAudio()
audio_url.clear(); audio_url.clear();
StandardPaths::SetPathValue("?audio", ""); config::path->SetToken("?audio", "");
AnnounceAudioClose(); AnnounceAudioClose();
} }
@ -229,12 +222,6 @@ bool AudioController::IsAudioOpen() const
return player && provider; return player && provider;
} }
wxString AudioController::GetAudioURL() const
{
return audio_url;
}
void AudioController::SetTimingController(AudioTimingController *new_controller) void AudioController::SetTimingController(AudioTimingController *new_controller)
{ {
if (timing_controller.get() != new_controller) { if (timing_controller.get() != new_controller) {
@ -259,13 +246,9 @@ void AudioController::OnTimingControllerUpdatedPrimaryRange()
void AudioController::OnSubtitlesSave() void AudioController::OnSubtitlesSave()
{ {
if (IsAudioOpen()) if (IsAudioOpen())
{ context->ass->SetScriptInfo("Audio URI", config::path->MakeRelative(audio_url, "?script").generic_string());
context->ass->SetScriptInfo("Audio URI", MakeRelativePath(audio_url, context->ass->filename));
}
else else
{
context->ass->SetScriptInfo("Audio URI", ""); context->ass->SetScriptInfo("Audio URI", "");
}
} }
void AudioController::PlayRange(const TimeRange &range) void AudioController::PlayRange(const TimeRange &range)
@ -402,13 +385,13 @@ int64_t AudioController::MillisecondsFromSamples(int64_t samples) const
return millisamples / sr; 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 start_sample = SamplesFromMilliseconds(range.begin());
int64_t end_sample = SamplesFromMilliseconds(range.end()); int64_t end_sample = SamplesFromMilliseconds(range.end());
if (filename.empty() || start_sample > provider->GetNumSamples() || range.length() == 0) return; 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()); std::ofstream& out(outfile.Get());
size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels(); size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels();

View File

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

View File

@ -963,7 +963,7 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
if (show_time) if (show_time)
{ {
AssTime new_label_time = TimeFromAbsoluteX(track_cursor_pos); 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; track_cursor_label_rect.x += new_pos - old_pos;
RefreshRect(track_cursor_label_rect, false); RefreshRect(track_cursor_label_rect, false);
} }

View File

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

View File

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

View File

@ -75,7 +75,7 @@ void OSSPlayer::OpenStream()
// Open device // Open device
wxString device = to_wx(OPT_GET("Player/Audio/OSS/Device")->GetString()); 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) { if (dspdev < 0) {
throw OSSError("OSS player: opening device failed", 0); throw OSSError("OSS player: opening device failed", 0);
} }

View File

@ -32,11 +32,8 @@
/// @ingroup audio_input /// @ingroup audio_input
/// ///
#include "config.h" #include "config.h"
#include <cstdint>
#include "audio_provider_avs.h" #include "audio_provider_avs.h"
#include "audio_provider_convert.h" #include "audio_provider_convert.h"
#include "audio_provider_dummy.h" #include "audio_provider_dummy.h"
@ -47,13 +44,13 @@
#include "audio_provider_ram.h" #include "audio_provider_ram.h"
#include "audio_controller.h" #include "audio_controller.h"
#include "compat.h"
#include "dialog_progress.h" #include "dialog_progress.h"
#include "frame_main.h" #include "frame_main.h"
#include "main.h" #include "main.h"
#include "options.h" #include "options.h"
#include "utils.h" #include "utils.h"
#include <libaegisub/fs.h>
#include <libaegisub/log.h> #include <libaegisub/log.h>
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const { 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; LOG_I("audio_provider") << "Using audio provider: " << name;
return provider; return provider;
} }
catch (agi::FileNotFoundError const& err) { catch (agi::fs::FileNotFound const& err) {
LOG_D("audio_provider") << err.GetChainedMessage(); LOG_D("audio_provider") << err.GetChainedMessage();
msg += name + ": " + err.GetMessage() + " not found.\n"; 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; provider_creator creator;
AudioProvider *provider = nullptr; AudioProvider *provider = nullptr;
@ -172,7 +169,7 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename) {
throw agi::AudioProviderOpenError(creator.msg, 0); throw agi::AudioProviderOpenError(creator.msg, 0);
if (creator.found_file) if (creator.found_file)
throw agi::AudioDataNotFoundError(creator.msg, 0); throw agi::AudioDataNotFoundError(creator.msg, 0);
throw agi::FileNotFoundError(from_wx(filename)); throw agi::fs::FileNotFound(filename);
} }
bool needsCache = provider->NeedsCache(); bool needsCache = provider->NeedsCache();
@ -206,4 +203,4 @@ void AudioProviderFactory::RegisterProviders() {
#endif #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_provider_avs.h"
#include "audio_controller.h" #include "audio_controller.h"
#include "charset_conv.h"
#include "compat.h"
#include "options.h" #include "options.h"
#include "standard_paths.h" #include "standard_paths.h"
#include "utils.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; this->filename = filename;
wxMutexLocker lock(avs_wrapper.GetMutex()); agi::acs::CheckFileRead(filename);
wxFileName fn(filename); std::lock_guard<std::mutex> lock(avs_wrapper.GetMutex());
if (!fn.FileExists())
throw agi::FileNotFoundError(from_wx(filename));
try { try {
IScriptEnvironment *env = avs_wrapper.GetEnv(); IScriptEnvironment *env = avs_wrapper.GetEnv();
// Include // Include
if (filename.EndsWith(".avs")) if (agi::fs::HasExtension(filename, "avs"))
LoadFromClip(env->Invoke("Import", env->SaveString(fn.GetShortPath().mb_str(csConvLocal)))); LoadFromClip(env->Invoke("Import", env->SaveString(agi::fs::ShortName(filename).c_str())));
// Use DirectShowSource // Use DirectShowSource
else { else {
const char * argnames[3] = { 0, "video", "audio" }; 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 // Load DirectShowSource.dll from app dir if it exists
wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll")); agi::fs::path dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll"));
if (dsspath.FileExists()) if (agi::fs::FileExists(dsspath))
env->Invoke("LoadPlugin", env->SaveString(dsspath.GetShortPath().mb_str(csConvLocal))); env->Invoke("LoadPlugin", env->SaveString(agi::fs::ShortName(dsspath).c_str()));
// Load audio with DSS if it exists // Load audio with DSS if it exists
if (env->FunctionExists("DirectShowSource")) 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; void FillBuffer(void *buf, int64_t start, int64_t count) const;
public: public:
AvisynthAudioProvider(wxString filename); AvisynthAudioProvider(agi::fs::path const& filename);
bool AreSamplesNativeEndian() const { return true; } bool AreSamplesNativeEndian() const { return true; }
bool NeedsCache() const { return true; } bool NeedsCache() const { return true; }

View File

@ -46,7 +46,7 @@ public:
} }
bool AreSamplesNativeEndian() const { return true; } 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 /// Anything integral -> 16 bit signed machine-endian audio converter

View File

@ -37,7 +37,9 @@
#include "audio_provider_dummy.h" #include "audio_provider_dummy.h"
#include "utils.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 * scheme ::= "dummy-audio" ":" signal-specifier "?" signal-parameters
@ -59,13 +61,11 @@
* in every channel even if one would be LFE. * in every channel even if one would be LFE.
* "ln", length of signal in samples. ln/sr gives signal length in seconds. * "ln", length of signal in samples. ln/sr gives signal length in seconds.
*/ */
DummyAudioProvider::DummyAudioProvider(wxString uri) DummyAudioProvider::DummyAudioProvider(agi::fs::path const& uri) {
{ if (!boost::starts_with(uri.string(), "dummy-audio:"))
wxURI parsed(uri); throw agi::fs::FileNotFound(std::string("Not a dummy audio URI"));
if (parsed.GetScheme() != "dummy-audio")
throw agi::FileNotFoundError("Not a dummy audio URI");
noise = parsed.GetPath() == "noise"; noise = boost::contains(uri.string(), ":noise?");
channels = 1; channels = 1;
sample_rate = 44100; sample_rate = 44100;
bytes_per_sample = 2; 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; void FillBuffer(void *buf, int64_t start, int64_t count) const;
public: public:
DummyAudioProvider(wxString uri); DummyAudioProvider(agi::fs::path const& uri);
bool AreSamplesNativeEndian() const { return true; } bool AreSamplesNativeEndian() const { return true; }
}; };

View File

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

View File

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

View File

@ -36,50 +36,43 @@
#include "audio_provider_hd.h" #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_controller.h"
#include "audio_provider_pcm.h" #include "audio_provider_pcm.h"
#include "compat.h" #include "compat.h"
#include "options.h" #include "options.h"
#include "standard_paths.h"
#include "utils.h" #include "utils.h"
namespace { #include <libaegisub/access.h>
wxString cache_dir() { #include <libaegisub/background_runner.h>
wxString path = to_wx(OPT_GET("Audio/Cache/HD/Location")->GetString()); #include <libaegisub/fs.h>
if (path == "default") #include <libaegisub/io.h>
path = "?temp/"; #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() { agi::fs::path cache_path() {
wxString pattern = to_wx(OPT_GET("Audio/Cache/HD/Name")->GetString()); std::string pattern = OPT_GET("Audio/Cache/HD/Name")->GetString();
if (pattern.Find("%02i") == wxNOT_FOUND) pattern = "audio%02i.tmp"; if (!boost::contains(pattern, "%02i")) pattern = "audio%02i.tmp";
boost::replace_all(pattern, "%02i", "%%%%-%%%%-%%%%-%%%%");
// Try from 00 to 99 return unique_path(cache_dir()/pattern);
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 "";
} }
/// A PCM audio provider for raw dumps with no header /// A PCM audio provider for raw dumps with no header
class RawAudioProvider : public PCMAudioProvider { class RawAudioProvider : public PCMAudioProvider {
public: public:
RawAudioProvider(wxString const& cache_filename, AudioProvider *src) RawAudioProvider(agi::fs::path const& cache_filename, AudioProvider *src)
: PCMAudioProvider(cache_filename) : PCMAudioProvider(cache_filename)
{ {
bytes_per_sample = src->GetBytesPerSample(); bytes_per_sample = src->GetBytesPerSample();
@ -110,30 +103,27 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::BackgroundRunner *br)
float_samples = source->AreSamplesFloat(); float_samples = source->AreSamplesFloat();
// Check free space // Check free space
wxDiskspaceSize_t freespace; if ((uint64_t)num_samples * channels * bytes_per_sample > agi::fs::FreeSpace(cache_dir()))
if (wxGetDiskSpace(cache_dir(), 0, &freespace)) { throw agi::AudioCacheOpenError("Not enough free disk space in " + cache_dir().string() + " to cache the audio", 0);
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);
}
diskCacheFilename = cache_path(); diskCacheFilename = cache_path();
try { 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)); br->Run(bind(&HDAudioProvider::FillCache, this, src, &out.Get(), std::placeholders::_1));
} }
cache_provider.reset(new RawAudioProvider(diskCacheFilename, src)); cache_provider.reset(new RawAudioProvider(diskCacheFilename, src));
} }
catch (...) { catch (...) {
wxRemoveFile(diskCacheFilename); agi::fs::Remove(diskCacheFilename);
throw; throw;
} }
} }
HDAudioProvider::~HDAudioProvider() { HDAudioProvider::~HDAudioProvider() {
cache_provider.reset(); // explicitly close the file so we can delete it 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 { void HDAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {

View File

@ -32,8 +32,6 @@
/// @ingroup audio_input /// @ingroup audio_input
/// ///
#include <iosfwd>
#include "include/aegisub/audio_provider.h" #include "include/aegisub/audio_provider.h"
#include <libaegisub/scoped_ptr.h> #include <libaegisub/scoped_ptr.h>
@ -45,7 +43,7 @@ namespace agi {
class HDAudioProvider : public AudioProvider { class HDAudioProvider : public AudioProvider {
/// Name of the file which the decoded audio is written to /// 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 /// Audio provider which reads from the decoded cache
agi::scoped_ptr<AudioProvider> cache_provider; 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 { 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); source->GetAudio(buf, start, count);
} }

View File

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

View File

@ -32,30 +32,26 @@
/// @ingroup audio_input /// @ingroup audio_input
/// ///
#include "config.h" #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 <cassert>
#include <cstdint> #include <cstdint>
#ifndef __WINDOWS__ #ifndef _WIN32
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
#include <wx/file.h> PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename)
#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)
: current_mapping(0) : current_mapping(0)
, mapping_start(0) , mapping_start(0)
, mapping_length(0) , mapping_length(0)
@ -73,13 +69,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
0); 0);
if (file_handle == INVALID_HANDLE_VALUE) if (file_handle == INVALID_HANDLE_VALUE)
throw agi::FileNotFoundError(from_wx(filename)); throw agi::fs::FileNotFound(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;
file_mapping = CreateFileMapping( file_mapping = CreateFileMapping(
file_handle, file_handle,
@ -91,24 +81,22 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
if (file_mapping == 0) if (file_mapping == 0)
throw agi::AudioProviderOpenError("Failed creating file mapping", 0); throw agi::AudioProviderOpenError("Failed creating file mapping", 0);
#else #else
, file_handle(open(filename.mb_str(*wxConvFileName), O_RDONLY), close) , file_handle(open(filename.c_str(), O_RDONLY), close)
{ {
if (file_handle == -1) if (file_handle == -1)
throw agi::FileNotFoundError(from_wx(filename)); throw agi::fs::FileNotFound(filename.string());
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;
#endif #endif
float_samples = false; 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 #ifdef _WIN32
if (current_mapping) if (current_mapping)
UnmapViewOfFile(current_mapping); UnmapViewOfFile(current_mapping);
@ -118,11 +106,9 @@ PCMAudioProvider::~PCMAudioProvider()
#endif #endif
} }
char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const {
{ if (range_start + range_length > file_size)
if (range_start + range_length > file_size) {
throw AudioDecodeError("Attempted to map beyond end of file"); throw AudioDecodeError("Attempted to map beyond end of file");
}
// Check whether the requested range is already visible // 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) { 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); current_mapping = mmap(0, mapping_length, PROT_READ, MAP_PRIVATE, file_handle, mapping_start);
#endif #endif
if (!current_mapping) { if (!current_mapping)
throw AudioDecodeError("Failed mapping a view of the file"); throw AudioDecodeError("Failed mapping a view of the file");
}
} }
assert(current_mapping); assert(current_mapping);
@ -186,8 +171,7 @@ char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t rang
return ((char*)current_mapping) + rel_ofs; 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 // Read blocks from the file
size_t index = 0; size_t index = 0;
while (count > 0 && index < index_points.size()) { while (count > 0 && index < index_points.size()) {
@ -268,10 +252,10 @@ class RiffWavPCMAudioProvider : public PCMAudioProvider {
public: public:
RiffWavPCMAudioProvider(const wxString &_filename) RiffWavPCMAudioProvider(agi::fs::path const& filename)
: PCMAudioProvider(_filename) : PCMAudioProvider(filename)
{ {
filename = _filename; this->filename = filename;
// Read header // Read header
void *filestart = EnsureRangeAccessible(0, sizeof(RIFFChunk)); void *filestart = EnsureRangeAccessible(0, sizeof(RIFFChunk));
@ -345,8 +329,7 @@ public:
} }
} }
bool AreSamplesNativeEndian() const bool AreSamplesNativeEndian() const {
{
// 8 bit samples don't consider endianness // 8 bit samples don't consider endianness
if (bytes_per_sample < 2) return true; if (bytes_per_sample < 2) return true;
// Otherwise test whether we're little endian // Otherwise test whether we're little endian
@ -411,17 +394,16 @@ class Wave64AudioProvider : public PCMAudioProvider {
uint64_t chunk_size; 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; return memcmp(guid1, guid2, 16) == 0;
} }
public: public:
Wave64AudioProvider(const wxString &_filename) Wave64AudioProvider(agi::fs::path const& filename)
: PCMAudioProvider(_filename) : PCMAudioProvider(filename)
{ {
filename = _filename; this->filename = filename;
int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk); 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 // 8 bit samples don't consider endianness
if (bytes_per_sample < 2) return true; if (bytes_per_sample < 2) return true;
// Otherwise test whether we're little endian // 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; bool wrong_file_type = true;
std::string msg; std::string msg;

View File

@ -34,9 +34,6 @@
#include <vector> #include <vector>
#include <wx/file.h>
#include <wx/thread.h>
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
@ -63,7 +60,7 @@ class PCMAudioProvider : public AudioProvider {
#endif #endif
protected: 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 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 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 // 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