mirror of
https://github.com/odrling/Aegisub
synced 2025-04-11 22:56:02 +02:00
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:
parent
10e06ac3f9
commit
1e0f08c0ed
@ -134,7 +134,6 @@
|
||||
<ClInclude Include="$(SrcDir)avisynth_wrap.h" />
|
||||
<ClInclude Include="$(SrcDir)base_grid.h" />
|
||||
<ClInclude Include="$(SrcDir)block_cache.h" />
|
||||
<ClInclude Include="$(SrcDir)charset_conv.h" />
|
||||
<ClInclude Include="$(SrcDir)charset_detect.h" />
|
||||
<ClInclude Include="$(SrcDir)colorspace.h" />
|
||||
<ClInclude Include="$(SrcDir)colour_button.h" />
|
||||
@ -321,7 +320,6 @@
|
||||
<ClCompile Include="$(SrcDir)auto4_lua_scriptreader.cpp" />
|
||||
<ClCompile Include="$(SrcDir)avisynth_wrap.cpp" />
|
||||
<ClCompile Include="$(SrcDir)base_grid.cpp" />
|
||||
<ClCompile Include="$(SrcDir)charset_conv.cpp" />
|
||||
<ClCompile Include="$(SrcDir)charset_detect.cpp" />
|
||||
<ClCompile Include="$(SrcDir)colorspace.cpp" />
|
||||
<ClCompile Include="$(SrcDir)colour_button.cpp" />
|
||||
|
@ -525,9 +525,6 @@
|
||||
<ClInclude Include="$(SrcDir)charset_detect.h">
|
||||
<Filter>Features\Import</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)charset_conv.h">
|
||||
<Filter>Features\Import</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)help_button.h">
|
||||
<Filter>Features\Help</Filter>
|
||||
</ClInclude>
|
||||
@ -1076,9 +1073,6 @@
|
||||
<ClCompile Include="$(SrcDir)subs_grid.cpp">
|
||||
<Filter>Main UI\Grid</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)charset_conv.cpp">
|
||||
<Filter>Features\Import</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)charset_detect.cpp">
|
||||
<Filter>Features\Import</Filter>
|
||||
</ClCompile>
|
||||
|
@ -17,20 +17,27 @@
|
||||
<!-- Project specific configuration -->
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SrcDir);$(SrcDir)include;$(AegisubContribBase)iconv\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>
|
||||
$(SrcDir);
|
||||
$(SrcDir)include;
|
||||
$(AegisubContribBase)iconv\include;
|
||||
%(AdditionalIncludeDirectories)
|
||||
</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>
|
||||
NOMINMAX;
|
||||
_WIN32_WINNT=0x0501;
|
||||
%(PreprocessorDefinitions)
|
||||
</PreprocessorDefinitions>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>lagi_pre.h</PrecompiledHeaderFile>
|
||||
<ForcedIncludeFiles>lagi_pre.h</ForcedIncludeFiles>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<!-- Source files -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(SrcDir)lagi_pre.h" />
|
||||
<ClInclude Include="$(SrcDir)config.h" />
|
||||
<ClInclude Include="$(SrcDir)common\charset_6937.h" />
|
||||
<ClInclude Include="$(SrcDir)common\charset_ucd.h" />
|
||||
<ClInclude Include="$(SrcDir)common\option_visit.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\access.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\charset.h" />
|
||||
@ -64,8 +71,13 @@
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\background_runner.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\color.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\spellchecker.h" />
|
||||
<ClInclude Include="..\..\libaegisub\include\libaegisub\calltip_provider.h" />
|
||||
<ClInclude Include="..\..\libaegisub\include\libaegisub\of_type_adaptor.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\fs.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\of_type_adaptor.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\path.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\fs_fwd.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\dispatch.h" />
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\split.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
|
||||
@ -78,7 +90,6 @@
|
||||
<ClCompile Include="$(SrcDir)common\charset.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\charset_6937.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\charset_conv.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\charset_ucd.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\hotkey.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\json.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\keyframe.cpp" />
|
||||
@ -96,8 +107,13 @@
|
||||
<ClCompile Include="$(SrcDir)ass\dialogue_parser.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\color.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\parser.cpp" />
|
||||
<ClCompile Include="..\..\libaegisub\common\calltip_provider.cpp" />
|
||||
<ClCompile Include="..\..\libaegisub\common\io.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\fs.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\io.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\path.cpp" />
|
||||
<ClCompile Include="$(SrcDir)windows\fs.cpp" />
|
||||
<ClCompile Include="$(SrcDir)windows\path_win.cpp" />
|
||||
<ClCompile Include="$(SrcDir)common\dispatch.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(SrcDir)include\libaegisub\charsets.def" />
|
||||
|
@ -26,9 +26,6 @@
|
||||
<ClInclude Include="$(SrcDir)common\charset_6937.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)common\charset_ucd.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)common\option_visit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -134,10 +131,25 @@
|
||||
<ClInclude Include="$(SrcDir)common\parser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\libaegisub\include\libaegisub\of_type_adaptor.h">
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\of_type_adaptor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\libaegisub\include\libaegisub\calltip_provider.h">
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\path.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\fs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\fs_fwd.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\dispatch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SrcDir)include\libaegisub\split.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@ -160,9 +172,6 @@
|
||||
<ClCompile Include="$(SrcDir)windows\charset_conv_win.cpp">
|
||||
<Filter>Source Files\Windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)common\charset_ucd.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)windows\log_win.cpp">
|
||||
<Filter>Source Files\Windows</Filter>
|
||||
</ClCompile>
|
||||
@ -220,10 +229,25 @@
|
||||
<ClCompile Include="$(SrcDir)common\parser.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\libaegisub\common\io.cpp">
|
||||
<ClCompile Include="$(SrcDir)common\io.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\libaegisub\common\calltip_provider.cpp">
|
||||
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)common\path.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)windows\path_win.cpp">
|
||||
<Filter>Source Files\Windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)windows\fs.cpp">
|
||||
<Filter>Source Files\Windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)common\fs.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SrcDir)common\dispatch.cpp">
|
||||
<Filter>Source Files\Common</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
@ -232,4 +256,4 @@
|
||||
<Filter>Header Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -25,23 +25,27 @@ SRC += \
|
||||
common/charset.cpp \
|
||||
common/charset_6937.cpp \
|
||||
common/charset_conv.cpp \
|
||||
common/charset_ucd.cpp \
|
||||
common/color.cpp \
|
||||
common/dispatch.cpp \
|
||||
common/fs.cpp \
|
||||
common/hotkey.cpp \
|
||||
common/io.cpp \
|
||||
common/json.cpp \
|
||||
common/keyframe.cpp \
|
||||
common/log.cpp \
|
||||
common/mru.cpp \
|
||||
common/option.cpp \
|
||||
common/option_visit.cpp \
|
||||
common/parser.cpp \
|
||||
common/util.cpp \
|
||||
common/log.cpp \
|
||||
common/path.cpp \
|
||||
common/thesaurus.cpp \
|
||||
common/util.cpp \
|
||||
common/vfr.cpp \
|
||||
unix/util.cpp \
|
||||
unix/access.cpp \
|
||||
unix/log.cpp
|
||||
unix/fs.cpp \
|
||||
unix/log.cpp \
|
||||
unix/path.cpp \
|
||||
unix/util.cpp
|
||||
|
||||
ifeq (yes, $(BUILD_DARWIN))
|
||||
SRC += osx/util.mm
|
||||
|
@ -16,18 +16,118 @@
|
||||
/// @brief Character set detection and manipulation utilities.
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include "charset_ucd.h"
|
||||
#include "libaegisub/charset.h"
|
||||
|
||||
namespace agi {
|
||||
namespace charset {
|
||||
#include "libaegisub/io.h"
|
||||
|
||||
std::string Detect(const std::string &file) {
|
||||
return UCDetect(file).Single();
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#ifndef _WIN32
|
||||
#define _X86_ 1
|
||||
#endif
|
||||
|
||||
#include "../../universalchardet/nscore.h"
|
||||
#include "../../universalchardet/nsUniversalDetector.h"
|
||||
#include "../../universalchardet/nsMBCSGroupProber.h"
|
||||
#include "../../universalchardet/nsCharSetProber.h"
|
||||
|
||||
namespace {
|
||||
using namespace agi::charset;
|
||||
|
||||
class UCDetect : public nsUniversalDetector {
|
||||
/// List of detected character sets
|
||||
CharsetListDetected list;
|
||||
|
||||
void Report(const char* aCharset) {}
|
||||
|
||||
public:
|
||||
/// @brief Detect character set of a file using UniversalCharDetect
|
||||
/// @param file File to check
|
||||
UCDetect(agi::fs::path const& file)
|
||||
: nsUniversalDetector(NS_FILTER_ALL)
|
||||
{
|
||||
{
|
||||
std::unique_ptr<std::ifstream> fp(agi::io::Open(file, true));
|
||||
|
||||
// If it's over 100 MB it's either binary or big enough that we won't
|
||||
// be able to do anything useful with it anyway
|
||||
fp->seekg(0, std::ios::end);
|
||||
if (fp->tellg() > 100 * 1024 * 1024) {
|
||||
list.emplace_back(1.f, "binary");
|
||||
return;
|
||||
}
|
||||
fp->seekg(0, std::ios::beg);
|
||||
|
||||
std::streamsize binaryish = 0;
|
||||
std::streamsize bytes = 0;
|
||||
|
||||
while (!mDone && *fp) {
|
||||
char buf[4096];
|
||||
fp->read(buf, sizeof(buf));
|
||||
std::streamsize read = fp->gcount();
|
||||
HandleData(buf, (PRUint32)read);
|
||||
|
||||
// A dumb heuristic to detect binary files
|
||||
if (!mDone) {
|
||||
bytes += read;
|
||||
for (std::streamsize i = 0; i < read; ++i) {
|
||||
if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
|
||||
++binaryish;
|
||||
}
|
||||
|
||||
if (binaryish > bytes / 8) {
|
||||
list.emplace_back(1.f, "binary");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DataEnd();
|
||||
|
||||
if (mDetectedCharset)
|
||||
list.emplace_back(1.f, mDetectedCharset);
|
||||
else {
|
||||
switch (mInputState) {
|
||||
case eHighbyte: {
|
||||
for (PRInt32 i=0; i<NUM_OF_CHARSET_PROBERS; i++) {
|
||||
if (!mCharSetProbers[i]) continue;
|
||||
|
||||
float conf = mCharSetProbers[i]->GetConfidence();
|
||||
if (conf > 0.01f)
|
||||
list.emplace_back(conf, mCharSetProbers[i]->GetCharSetName());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ePureAscii:
|
||||
list.emplace_back(1.f, "US-ASCII");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw UnknownCharset("Unknown character set.");
|
||||
}
|
||||
|
||||
if (list.empty() && (mInputState == eHighbyte))
|
||||
throw UnknownCharset("Unknown character set.");
|
||||
|
||||
typedef std::pair<float, std::string> const& result;
|
||||
sort(begin(list), end(list), [](result lft, result rgt) { return lft.first > rgt.first; });
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Detect character set of a file using UniversalCharDet
|
||||
CharsetListDetected List() const { return list; }
|
||||
};
|
||||
}
|
||||
|
||||
CharsetListDetected DetectAll(const std::string& file) {
|
||||
return UCDetect(file).List();
|
||||
}
|
||||
namespace agi { namespace charset {
|
||||
std::string Detect(agi::fs::path const& file) {
|
||||
return DetectAll(file).front().second;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace agi
|
||||
CharsetListDetected DetectAll(agi::fs::path const& file) {
|
||||
return UCDetect(file).List();
|
||||
}
|
||||
} }
|
||||
|
@ -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
|
@ -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
|
98
aegisub/libaegisub/common/dispatch.cpp
Normal file
98
aegisub/libaegisub/common/dispatch.cpp
Normal 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);
|
||||
}
|
||||
|
||||
} }
|
106
aegisub/libaegisub/common/fs.cpp
Normal file
106
aegisub/libaegisub/common/fs.cpp
Normal 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);
|
||||
}
|
||||
} }
|
@ -18,11 +18,6 @@
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include "libaegisub/hotkey.h"
|
||||
|
||||
#include "libaegisub/cajun/writer.h"
|
||||
@ -31,8 +26,12 @@
|
||||
#include "libaegisub/json.h"
|
||||
#include "libaegisub/log.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <tuple>
|
||||
|
||||
namespace agi {
|
||||
namespace hotkey {
|
||||
@ -50,12 +49,12 @@ void Hotkey::ComboInsert(Combo const& combo) {
|
||||
cmd_map.insert(make_pair(combo.CmdName(), combo));
|
||||
}
|
||||
|
||||
Hotkey::Hotkey(const std::string &file, const std::string &default_config)
|
||||
Hotkey::Hotkey(agi::fs::path const& file, const std::string &default_config)
|
||||
: config_file(file)
|
||||
{
|
||||
LOG_D("hotkey/init") << "Generating hotkeys.";
|
||||
|
||||
json::Object object(agi::json_util::file(config_file, default_config));
|
||||
const json::Object object(agi::json_util::file(config_file, default_config));
|
||||
for (auto const& hotkey_context : object)
|
||||
BuildHotkey(hotkey_context.first, hotkey_context.second);
|
||||
}
|
||||
@ -118,7 +117,7 @@ std::vector<std::string> Hotkey::GetHotkeys(const std::string &context, const st
|
||||
for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) {
|
||||
std::string ctext = it->second.Context();
|
||||
if (ctext == "Always" || ctext == "Default" || ctext == context)
|
||||
ret.push_back(it->second.StrMenu());
|
||||
ret.emplace_back(it->second.StrMenu());
|
||||
}
|
||||
|
||||
sort(ret.begin(), ret.end());
|
||||
|
@ -16,49 +16,25 @@
|
||||
/// @brief Windows IO methods.
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "libaegisub/io.h"
|
||||
|
||||
#include <libaegisub/access.h>
|
||||
#include <libaegisub/charset_conv_win.h>
|
||||
#include "libaegisub/io.h"
|
||||
#include "libaegisub/fs.h"
|
||||
#include "libaegisub/log.h"
|
||||
#include "libaegisub/path.h"
|
||||
#include "libaegisub/util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define snprintf sprintf_s
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
std::string make_temp_name(std::string const& filename) {
|
||||
char tmp[1024];
|
||||
snprintf(tmp, sizeof tmp, "_tmp_%lld", (long long)time(0));
|
||||
|
||||
std::string::size_type pos = filename.rfind('.');
|
||||
if (pos == std::string::npos)
|
||||
return filename + tmp;
|
||||
|
||||
return filename.substr(0, pos) + tmp + filename.substr(pos);
|
||||
}
|
||||
}
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
namespace agi {
|
||||
namespace io {
|
||||
|
||||
using agi::charset::ConvertW;
|
||||
|
||||
#ifndef _WIN32
|
||||
#define ConvertW
|
||||
#endif
|
||||
|
||||
std::ifstream* Open(const std::string &file, bool binary) {
|
||||
std::ifstream* Open(fs::path const& file, bool binary) {
|
||||
LOG_D("agi/io/open/file") << file;
|
||||
acs::CheckFileRead(file);
|
||||
|
||||
std::ifstream *stream = new std::ifstream(ConvertW(file).c_str(), (binary ? std::ios::binary : std::ios::in));
|
||||
auto stream = new boost::filesystem::ifstream(file, (binary ? std::ios::binary : std::ios::in));
|
||||
|
||||
if (stream->fail()) {
|
||||
delete stream;
|
||||
@ -68,34 +44,30 @@ std::ifstream* Open(const std::string &file, bool binary) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
Save::Save(const std::string& file, bool binary)
|
||||
Save::Save(fs::path const& file, bool binary)
|
||||
: file_name(file)
|
||||
, tmp_name(make_temp_name(file))
|
||||
, tmp_name(unique_path(file.parent_path()/(file.stem().string() + "_tmp_%%%%." + file.extension().string())))
|
||||
{
|
||||
LOG_D("agi/io/save/file") << file;
|
||||
const std::string pwd = util::DirName(file);
|
||||
|
||||
acs::CheckDirWrite(pwd);
|
||||
acs::CheckDirWrite(file.parent_path());
|
||||
|
||||
try {
|
||||
acs::CheckFileWrite(file);
|
||||
} catch (FileNotFoundError const&) {
|
||||
// If the file doesn't exist we create a 0 byte file, this so so
|
||||
// util::Rename will find it, and to let users know something went
|
||||
// wrong by leaving a 0 byte file.
|
||||
std::ofstream(ConvertW(file).c_str());
|
||||
}
|
||||
catch (fs::FileNotFound const&) {
|
||||
// Not an error
|
||||
}
|
||||
|
||||
fp = new std::ofstream(ConvertW(tmp_name).c_str(), binary ? std::ios::binary : std::ios::out);
|
||||
fp = new boost::filesystem::ofstream(tmp_name, binary ? std::ios::binary : std::ios::out);
|
||||
if (!fp->good()) {
|
||||
delete fp;
|
||||
throw agi::FileNotAccessibleError("Could not create temporary file at: " + tmp_name);
|
||||
throw fs::WriteDenied(tmp_name);
|
||||
}
|
||||
}
|
||||
|
||||
Save::~Save() {
|
||||
delete fp;
|
||||
util::Rename(tmp_name, file_name);
|
||||
delete fp; // Explicitly delete to unlock file on Windows
|
||||
fs::Rename(tmp_name, file_name);
|
||||
}
|
||||
|
||||
std::ofstream& Save::Get() {
|
||||
|
@ -18,21 +18,22 @@
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include "libaegisub/json.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "libaegisub/fs.h"
|
||||
#include "libaegisub/io.h"
|
||||
#include "libaegisub/json.h"
|
||||
#include "libaegisub/log.h"
|
||||
#include "libaegisub/scoped_ptr.h"
|
||||
|
||||
|
||||
namespace agi {
|
||||
namespace json_util {
|
||||
|
||||
json::UnknownElement parse(std::istream *stream) {
|
||||
try {
|
||||
agi::scoped_ptr<std::istream> stream_deleter(stream);
|
||||
std::unique_ptr<std::istream> stream_deleter(stream);
|
||||
|
||||
json::UnknownElement root;
|
||||
json::Reader::Read(root, *stream);
|
||||
@ -46,15 +47,15 @@ json::UnknownElement parse(std::istream *stream) {
|
||||
}
|
||||
}
|
||||
|
||||
json::UnknownElement file(const std::string &file) {
|
||||
json::UnknownElement file(agi::fs::path const& file) {
|
||||
return parse(io::Open(file));
|
||||
}
|
||||
|
||||
json::UnknownElement file(const std::string &file, const std::string &default_config) {
|
||||
json::UnknownElement file(agi::fs::path const& file, const std::string &default_config) {
|
||||
try {
|
||||
return parse(io::Open(file));
|
||||
}
|
||||
catch (FileNotFoundError const&) {
|
||||
catch (fs::FileNotFound const&) {
|
||||
// Not an error
|
||||
return parse(new std::istringstream(default_config));
|
||||
}
|
||||
|
@ -17,16 +17,15 @@
|
||||
/// @ingroup libaegisub
|
||||
///
|
||||
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include "libaegisub/keyframe.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
#include "libaegisub/io.h"
|
||||
#include "libaegisub/line_iterator.h"
|
||||
#include "libaegisub/keyframe.h"
|
||||
#include "libaegisub/vfr.h"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/range/algorithm.hpp>
|
||||
@ -77,7 +76,7 @@ char x264(std::string const& line) {
|
||||
}
|
||||
|
||||
namespace agi { namespace keyframe {
|
||||
void Save(std::string const& filename, std::vector<int> const& keyframes) {
|
||||
void Save(agi::fs::path const& filename, std::vector<int> const& keyframes) {
|
||||
io::Save file(filename);
|
||||
std::ofstream& of = file.Get();
|
||||
of << "# keyframe format v1" << std::endl;
|
||||
@ -85,7 +84,7 @@ void Save(std::string const& filename, std::vector<int> const& keyframes) {
|
||||
boost::copy(keyframes, std::ostream_iterator<int>(of, "\n"));
|
||||
}
|
||||
|
||||
std::vector<int> Load(std::string const& filename) {
|
||||
std::vector<int> Load(agi::fs::path const& filename) {
|
||||
std::unique_ptr<std::ifstream> file(io::Open(filename));
|
||||
std::istream &is(*file);
|
||||
|
||||
|
@ -18,21 +18,20 @@
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include "libaegisub/log.h"
|
||||
|
||||
#include "libaegisub/cajun/elements.h"
|
||||
#include "libaegisub/cajun/writer.h"
|
||||
#include "libaegisub/io.h"
|
||||
#include "libaegisub/log.h"
|
||||
#include "libaegisub/types.h"
|
||||
#include "libaegisub/util.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/range/algorithm.hpp>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace agi {
|
||||
namespace log {
|
||||
@ -109,7 +108,7 @@ Message::~Message() {
|
||||
agi::log::log->log(sm);
|
||||
}
|
||||
|
||||
JsonEmitter::JsonEmitter(std::string const& directory, const agi::log::LogSink *log_sink)
|
||||
JsonEmitter::JsonEmitter(agi::fs::path const& directory, const agi::log::LogSink *log_sink)
|
||||
: directory(directory)
|
||||
, log_sink(log_sink)
|
||||
{
|
||||
@ -146,9 +145,7 @@ JsonEmitter::~JsonEmitter() {
|
||||
timeval_close.push_back((int64_t)time_close.tv_sec);
|
||||
timeval_close.push_back((int64_t)time_close.tv_usec);
|
||||
|
||||
std::stringstream str;
|
||||
str << directory << time_start.tv_sec << ".json";
|
||||
json::Writer::Write(root, io::Save(str.str()).Get());
|
||||
json::Writer::Write(root, io::Save(directory/(std::to_string(time_start.tv_sec) + ".json")).Get());
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
namespace agi {
|
||||
|
||||
MRUManager::MRUManager(std::string const& config, std::string const& default_config, agi::Options *options)
|
||||
MRUManager::MRUManager(agi::fs::path const& config, std::string const& default_config, agi::Options *options)
|
||||
: config_name(config)
|
||||
, options(options)
|
||||
{
|
||||
@ -60,16 +60,22 @@ MRUManager::MRUListMap &MRUManager::Find(std::string const& key) {
|
||||
return index->second;
|
||||
}
|
||||
|
||||
void MRUManager::Add(std::string const& key, std::string const& entry) {
|
||||
void MRUManager::Add(std::string const& key, agi::fs::path const& entry) {
|
||||
MRUListMap &map = Find(key);
|
||||
map.remove(entry);
|
||||
map.push_front(entry);
|
||||
Prune(key, map);
|
||||
auto it = find(begin(map), end(map), entry);
|
||||
if (it == begin(map) && it != end(map))
|
||||
return;
|
||||
if (it != end(map))
|
||||
map.splice(begin(map), map, it);
|
||||
else {
|
||||
map.push_front(entry);
|
||||
Prune(key, map);
|
||||
}
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
void MRUManager::Remove(std::string const& key, std::string const& entry) {
|
||||
void MRUManager::Remove(std::string const& key, agi::fs::path const& entry) {
|
||||
Find(key).remove(entry);
|
||||
|
||||
Flush();
|
||||
@ -79,7 +85,7 @@ const MRUManager::MRUListMap* MRUManager::Get(std::string const& key) {
|
||||
return &Find(key);
|
||||
}
|
||||
|
||||
std::string const& MRUManager::GetEntry(std::string const& key, const size_t entry) {
|
||||
agi::fs::path const& MRUManager::GetEntry(std::string const& key, const size_t entry) {
|
||||
const MRUManager::MRUListMap *const map = Get(key);
|
||||
|
||||
if (entry >= map->size())
|
||||
@ -93,7 +99,8 @@ void MRUManager::Flush() {
|
||||
|
||||
for (auto const& mru_map : mru) {
|
||||
json::Array &array = out[mru_map.first];
|
||||
array.insert(array.end(), mru_map.second.begin(), mru_map.second.end());
|
||||
transform(begin(mru_map.second), end(mru_map.second),
|
||||
back_inserter(array), [](agi::fs::path const& p) { return p.string(); });
|
||||
}
|
||||
|
||||
json::Writer::Write(out, io::Save(config_name).Get());
|
||||
@ -116,7 +123,8 @@ void MRUManager::Prune(std::string const& key, MRUListMap& map) const {
|
||||
/// @param array json::Array of values.
|
||||
void MRUManager::Load(std::string const& key, const json::Array& array) {
|
||||
try {
|
||||
copy(array.begin(), array.end(), back_inserter(mru[key]));
|
||||
transform(begin(array), end(array),
|
||||
back_inserter(mru[key]), [](std::string const& s) { return s; });
|
||||
}
|
||||
catch (json::Exception const&) {
|
||||
// Out of date MRU file; just discard the data and skip it
|
||||
|
@ -20,14 +20,11 @@
|
||||
|
||||
#include "libaegisub/option.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "libaegisub/cajun/reader.h"
|
||||
#include "libaegisub/cajun/writer.h"
|
||||
#include "libaegisub/cajun/elements.h"
|
||||
|
||||
#include "libaegisub/fs.h"
|
||||
#include "libaegisub/io.h"
|
||||
#include "libaegisub/log.h"
|
||||
#include "libaegisub/option_value.h"
|
||||
@ -35,6 +32,9 @@
|
||||
#include "option_visit.h"
|
||||
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
/// @brief Write an option to a json object
|
||||
@ -66,7 +66,7 @@ namespace {
|
||||
|
||||
namespace agi {
|
||||
|
||||
Options::Options(const std::string &file, const std::string& default_config, const OptionSetting setting)
|
||||
Options::Options(agi::fs::path const& file, const std::string& default_config, const OptionSetting setting)
|
||||
: config_file(file)
|
||||
, setting(setting)
|
||||
{
|
||||
@ -88,21 +88,17 @@ void Options::ConfigNext(std::istream& stream) {
|
||||
}
|
||||
|
||||
void Options::ConfigUser() {
|
||||
std::unique_ptr<std::istream> stream;
|
||||
|
||||
try {
|
||||
stream.reset(agi::io::Open(config_file));
|
||||
} catch (const FileNotFoundError&) {
|
||||
std::unique_ptr<std::istream> stream(io::Open(config_file));
|
||||
LoadConfig(*stream, true);
|
||||
}
|
||||
catch (fs::FileNotFound const&) {
|
||||
return;
|
||||
}
|
||||
|
||||
/// @todo Handle other errors such as parsing and notifying the user.
|
||||
LoadConfig(*stream, true);
|
||||
}
|
||||
|
||||
void Options::LoadConfig(std::istream& stream, bool ignore_errors) {
|
||||
/// @todo Store all previously loaded configs in an array for bug report purposes,
|
||||
/// this is just a temp stub.
|
||||
json::UnknownElement config_root;
|
||||
|
||||
try {
|
||||
|
150
aegisub/libaegisub/common/path.cpp
Normal file
150
aegisub/libaegisub/common/path.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -26,16 +26,16 @@
|
||||
#include <boost/phoenix/operator/comparison.hpp>
|
||||
#include <boost/phoenix/core/argument.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
|
||||
using boost::phoenix::placeholders::_1;
|
||||
|
||||
namespace agi {
|
||||
|
||||
Thesaurus::Thesaurus(std::string const& dat_path, std::string const& idx_path)
|
||||
Thesaurus::Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path)
|
||||
: dat(io::Open(dat_path))
|
||||
{
|
||||
scoped_ptr<std::ifstream> idx(io::Open(idx_path));
|
||||
std::unique_ptr<std::ifstream> idx(io::Open(idx_path));
|
||||
|
||||
std::string encoding_name;
|
||||
getline(*idx, encoding_name);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011, Amar Takhar <verm@aegisub.org>
|
||||
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@ -11,36 +11,26 @@
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file util.cpp
|
||||
/// @brief Unix utility methods.
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include "../config.h"
|
||||
|
||||
#include "libaegisub/util.h"
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <ctime>
|
||||
|
||||
namespace agi {
|
||||
namespace util {
|
||||
namespace agi { namespace util {
|
||||
|
||||
void str_lower(std::string &str) {
|
||||
boost::to_lower(str);
|
||||
std::string strftime(const char *fmt, const tm *tmptr) {
|
||||
if (!tmptr) {
|
||||
time_t t = time(nullptr);
|
||||
tmptr = localtime(&t);
|
||||
}
|
||||
|
||||
char buff[65536];
|
||||
::strftime(buff, sizeof buff, fmt, tmptr);
|
||||
return buff;
|
||||
}
|
||||
|
||||
int strtoi(std::string const& str) {
|
||||
errno = 0;
|
||||
long l = strtol(str.c_str(), nullptr, 10);
|
||||
|
||||
if ((errno == ERANGE) || (l < INT_MIN) || (l > INT_MAX))
|
||||
return 0;
|
||||
|
||||
return (int)l;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace agi
|
||||
} }
|
||||
|
@ -16,10 +16,13 @@
|
||||
/// @brief Framerate handling of all sorts
|
||||
/// @ingroup libaegisub video_input
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include "libaegisub/vfr.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
@ -199,7 +202,7 @@ Framerate &Framerate::operator=(double fps) {
|
||||
return *this = Framerate(fps);
|
||||
}
|
||||
|
||||
Framerate::Framerate(std::string const& filename)
|
||||
Framerate::Framerate(fs::path const& filename)
|
||||
: denominator(default_denominator)
|
||||
, numerator(0)
|
||||
{
|
||||
@ -221,7 +224,7 @@ Framerate::Framerate(std::string const& filename)
|
||||
throw UnknownFormat(line);
|
||||
}
|
||||
|
||||
void Framerate::Save(std::string const& filename, int length) const {
|
||||
void Framerate::Save(fs::path const& filename, int length) const {
|
||||
agi::io::Save file(filename);
|
||||
std::ofstream &out = file.Get();
|
||||
|
||||
|
@ -17,17 +17,11 @@
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <libaegisub/exception.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
namespace agi {
|
||||
namespace acs {
|
||||
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(Fatal, FileSystemError, "filesystem/fatal");
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(NotAFile, FileSystemError, "filesystem/not_a_file")
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(NotADirectory, FileSystemError, "filesystem/not_a_directory")
|
||||
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(Read, FileNotAccessibleError, "filesystem/not_accessible/read_permission")
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(Write, FileNotAccessibleError, "filesystem/not_accessible/write_permission")
|
||||
|
||||
enum Type {
|
||||
FileRead,
|
||||
DirRead,
|
||||
@ -35,13 +29,13 @@ enum Type {
|
||||
DirWrite
|
||||
};
|
||||
|
||||
void Check(const std::string &file, acs::Type);
|
||||
void Check(fs::path const& file, acs::Type);
|
||||
|
||||
void CheckFileRead(const std::string &file);
|
||||
void CheckDirRead(const std::string &dir);
|
||||
inline void CheckFileRead(fs::path const& file) { Check(file, acs::FileRead); }
|
||||
inline void CheckFileWrite(fs::path const& file) { Check(file, acs::FileWrite); }
|
||||
|
||||
void CheckFileWrite(const std::string &file);
|
||||
void CheckDirWrite(const std::string &dir);
|
||||
inline void CheckDirRead(fs::path const& dir) { Check(dir, acs::DirRead); }
|
||||
inline void CheckDirWrite(fs::path const& dir) { Check(dir, acs::DirWrite); }
|
||||
|
||||
} // namespace axs
|
||||
} // namespace agi
|
||||
|
@ -16,11 +16,11 @@
|
||||
/// @brief Character set detection and manipulation utilities.
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <libaegisub/exception.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <libaegisub/exception.h>
|
||||
|
||||
namespace agi {
|
||||
/// Character set conversion and detection.
|
||||
@ -35,12 +35,12 @@ typedef std::vector<std::pair<float, std::string>> CharsetListDetected;
|
||||
/// @brief Return a complete list of detected character sets ordered by precedence.
|
||||
/// @param file File to check
|
||||
/// @return List of possible charsets sorted by probability
|
||||
CharsetListDetected DetectAll(std::string const& file);
|
||||
CharsetListDetected DetectAll(agi::fs::path const& file);
|
||||
|
||||
/// @brief Returns the character set with the highest confidence
|
||||
/// @param file File to check
|
||||
/// @return Detected character set.
|
||||
std::string Detect(const std::string &file);
|
||||
std::string Detect(agi::fs::path const& file);
|
||||
|
||||
} // namespace util
|
||||
} // namespace agi
|
||||
|
@ -23,5 +23,8 @@ namespace agi {
|
||||
/// Convert a UTF-8 string to a string suitable for use with Win32 API functions
|
||||
std::wstring ConvertW(std::string const& src);
|
||||
std::string ConvertW(std::wstring const& src);
|
||||
|
||||
/// Convert a UTF-16 string to the local charset
|
||||
std::string ConvertLocal(std::wstring const& src);
|
||||
}
|
||||
}
|
||||
|
50
aegisub/libaegisub/include/libaegisub/dispatch.h
Normal file
50
aegisub/libaegisub/include/libaegisub/dispatch.h
Normal 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();
|
||||
}
|
||||
}
|
@ -37,10 +37,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/// @see aegisub.h
|
||||
namespace agi {
|
||||
|
||||
|
||||
/// @class Exception
|
||||
/// @brief Base class for all exceptions in Aegisub.
|
||||
///
|
||||
@ -90,7 +87,6 @@ namespace agi {
|
||||
/// could not be opened for reading". This is the purpose of the "inner"
|
||||
/// exceptions.
|
||||
class Exception {
|
||||
|
||||
/// The error message
|
||||
std::string message;
|
||||
|
||||
@ -98,16 +94,13 @@ namespace agi {
|
||||
std::shared_ptr<Exception> inner;
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Protected constructor initialising members
|
||||
/// @param msg The error message
|
||||
/// @param inr The inner exception, optional
|
||||
///
|
||||
/// Deriving classes should always use this constructor for initialising
|
||||
/// the base class.
|
||||
Exception(const std::string &msg, const Exception *inr = 0)
|
||||
: message(msg)
|
||||
{
|
||||
Exception(const std::string &msg, const Exception *inr = 0) : message(msg) {
|
||||
if (inr)
|
||||
inner.reset(inr->Copy());
|
||||
}
|
||||
@ -120,9 +113,7 @@ namespace agi {
|
||||
|
||||
public:
|
||||
/// @brief Destructor
|
||||
virtual ~Exception()
|
||||
{
|
||||
}
|
||||
virtual ~Exception() { }
|
||||
|
||||
/// @brief Get the outer exception error message
|
||||
/// @return Error message
|
||||
@ -147,7 +138,6 @@ namespace agi {
|
||||
/// level are [a-z0-9_].
|
||||
virtual const char * GetName() const = 0;
|
||||
|
||||
|
||||
/// @brief Convert to char array as the error message
|
||||
/// @return The error message
|
||||
operator const char * () { return GetMessage().c_str(); }
|
||||
@ -164,16 +154,12 @@ namespace agi {
|
||||
virtual Exception *Copy() const = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @brief Convenience macro to include the current location in code
|
||||
///
|
||||
/// Intended for use in error messages where it can sometimes be convenient to
|
||||
/// indicate the exact position the error occurred at.
|
||||
#define AG_WHERE " (at " __FILE__ ":" #__LINE__ ")"
|
||||
|
||||
|
||||
|
||||
/// @brief Convenience macro for declaring exceptions with no support for inner exception
|
||||
/// @param classname Name of the exception class to declare
|
||||
/// @param baseclass Class to derive from
|
||||
@ -231,7 +217,6 @@ namespace agi {
|
||||
classname(const std::string &msg, Exception *inner) : baseclass(msg, inner) { } \
|
||||
};
|
||||
|
||||
|
||||
/// @class agi::UserCancelException
|
||||
/// @extends agi::Exception
|
||||
/// @brief Exception for "user cancel" events
|
||||
@ -246,7 +231,6 @@ namespace agi {
|
||||
/// code in question.
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(UserCancelException,Exception,"nonerror/user_cancel")
|
||||
|
||||
|
||||
/// @class agi::InternalError
|
||||
/// @extends agi:Exception
|
||||
/// @brief Errors that should never happen and point to some invalid assumption in the code
|
||||
@ -258,42 +242,17 @@ namespace agi {
|
||||
/// and eventually cause an abort().
|
||||
DEFINE_SIMPLE_EXCEPTION(InternalError, Exception, "internal_error")
|
||||
|
||||
|
||||
/// @class agi::FileSystemError
|
||||
/// @extends agi::Exception
|
||||
/// @brief Base class for errors related to the file system
|
||||
/// @class agi::EnvironmentError
|
||||
/// @extends agi:Exception
|
||||
/// @brief The execution environment is broken in some fundamental way
|
||||
///
|
||||
/// This base class can not be instantiated.
|
||||
/// File system errors do not support inner exceptions, as they are always originating
|
||||
/// causes for errors.
|
||||
DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError,Exception)
|
||||
|
||||
/// @class agi::FileNotAccessibleError
|
||||
/// @extends agi::FileSystemError
|
||||
/// @brief A file can't be accessed for some reason
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(FileNotAccessibleError,FileSystemError,"filesystem/not_accessible")
|
||||
|
||||
|
||||
/// @class FileNotFoundError
|
||||
/// @brief A file can't be accessed because there's no file by the given name
|
||||
class FileNotFoundError : public FileNotAccessibleError {
|
||||
public:
|
||||
|
||||
/// @brief Constructor, automatically builds the error message
|
||||
/// @param filename Name of the file that could not be found
|
||||
FileNotFoundError(const std::string &filename) : FileNotAccessibleError(std::string("File not found: ") + filename) { }
|
||||
|
||||
// Not documented, see agi::Exception class
|
||||
const char * GetName() const { return "filesystem/not_accessible/not_found"; }
|
||||
|
||||
// Not documented, see agi::Exception class
|
||||
Exception * Copy() const { return new FileNotFoundError(*this); }
|
||||
};
|
||||
|
||||
/// Throw an environment error when a call to the platform API has failed
|
||||
/// in some way that should normally never happen or suggests that the
|
||||
/// runtime environment is too insane to support.
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(EnvironmentError, Exception, "environment_error")
|
||||
|
||||
/// @class agi::InvalidInputException
|
||||
/// @extends agi::Exception
|
||||
/// @brief Some input data were invalid and could not be processed
|
||||
DEFINE_BASE_EXCEPTION(InvalidInputException,Exception)
|
||||
|
||||
DEFINE_BASE_EXCEPTION(InvalidInputException, Exception)
|
||||
}
|
||||
|
177
aegisub/libaegisub/include/libaegisub/fs.h
Normal file
177
aegisub/libaegisub/include/libaegisub/fs.h
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
18
aegisub/libaegisub/include/libaegisub/fs_fwd.h
Normal file
18
aegisub/libaegisub/include/libaegisub/fs_fwd.h
Normal 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; } }
|
@ -16,11 +16,13 @@
|
||||
/// @brief Hotkey handler
|
||||
/// @ingroup hotkey menu event window
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/signal.h>
|
||||
|
||||
namespace json {
|
||||
@ -75,9 +77,9 @@ public:
|
||||
/// Map to hold Combo instances
|
||||
typedef std::multimap<std::string, Combo> HotkeyMap;
|
||||
private:
|
||||
HotkeyMap str_map; ///< String representation -> Combo
|
||||
HotkeyMap cmd_map; ///< Command name -> Combo
|
||||
const std::string config_file; ///< Default user config location.
|
||||
HotkeyMap str_map; ///< String representation -> Combo
|
||||
HotkeyMap cmd_map; ///< Command name -> Combo
|
||||
const agi::fs::path config_file; ///< Default user config location.
|
||||
|
||||
/// Build hotkey map.
|
||||
/// @param context Context being parsed.
|
||||
@ -97,7 +99,7 @@ public:
|
||||
/// Constructor
|
||||
/// @param file Location of user config file.
|
||||
/// @param default_config Default config.
|
||||
Hotkey(const std::string &file, const std::string &default_config);
|
||||
Hotkey(agi::fs::path const& file, const std::string &default_config);
|
||||
|
||||
/// Scan for a matching key.
|
||||
/// @param context Context requested.
|
||||
|
@ -16,10 +16,11 @@
|
||||
/// @brief Public interface for IO methods.
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include <libaegisub/exception.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace agi {
|
||||
namespace io {
|
||||
@ -27,15 +28,15 @@ namespace agi {
|
||||
DEFINE_BASE_EXCEPTION_NOINNER(IOError, Exception)
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(IOFatal, IOError, "io/fatal")
|
||||
|
||||
std::ifstream* Open(const std::string &file, bool binary = false);
|
||||
std::ifstream* Open(fs::path const& file, bool binary = false);
|
||||
|
||||
class Save {
|
||||
std::ofstream *fp;
|
||||
const std::string file_name;
|
||||
const std::string tmp_name;
|
||||
const fs::path file_name;
|
||||
const fs::path tmp_name;
|
||||
|
||||
public:
|
||||
Save(const std::string& file, bool binary = false);
|
||||
Save(fs::path const& file, bool binary = false);
|
||||
~Save();
|
||||
std::ofstream& Get();
|
||||
};
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <libaegisub/cajun/reader.h>
|
||||
#include <libaegisub/cajun/elements.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
namespace agi {
|
||||
namespace json_util {
|
||||
@ -30,14 +31,13 @@ json::UnknownElement parse(std::istream *stream);
|
||||
/// Parse a JSON file.
|
||||
/// @param file Path JSON to file
|
||||
/// @return json::UnknownElement
|
||||
json::UnknownElement file(const std::string &file);
|
||||
json::UnknownElement file(agi::fs::path const& file);
|
||||
|
||||
/// Parse a json stream, with default handler.
|
||||
/// @param file Path to JSON file.
|
||||
/// @param Default config file to load incase of nonexistent file
|
||||
/// @return json::UnknownElement
|
||||
json::UnknownElement file(const std::string &file, const std::string &default_config);
|
||||
|
||||
json::UnknownElement file(agi::fs::path const& file, const std::string &default_config);
|
||||
|
||||
} // namespace json_util
|
||||
} // namespace agi
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "exception.h"
|
||||
#include "fs_fwd.h"
|
||||
|
||||
namespace agi {
|
||||
namespace vfr { class Framerate; }
|
||||
@ -27,11 +28,12 @@ namespace agi {
|
||||
/// @brief Load a keyframe file
|
||||
/// @param filename File to load
|
||||
/// @return List of frame numbers which are keyframes
|
||||
std::vector<int> Load(std::string const& filename);
|
||||
std::vector<int> Load(agi::fs::path const& filename);
|
||||
|
||||
/// @brief Save keyframes to a file
|
||||
/// @param filename File to save to
|
||||
/// @param keyframes List of keyframes to save
|
||||
void Save(std::string const& filename, std::vector<int> const& keyframes);
|
||||
void Save(agi::fs::path const& filename, std::vector<int> const& keyframes);
|
||||
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(Error, Exception, "keyframe/error")
|
||||
}
|
||||
|
@ -146,6 +146,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Enable range-based for
|
||||
template<typename T>
|
||||
line_iterator<T>& begin(line_iterator<T>& it) { return it; }
|
||||
|
||||
template<typename T>
|
||||
line_iterator<T> end(line_iterator<T>&) { return agi::line_iterator<T>(); }
|
||||
|
||||
template<class OutputType>
|
||||
void line_iterator<OutputType>::getline(std::string &str) {
|
||||
union {
|
||||
|
@ -16,12 +16,17 @@
|
||||
/// @brief Logging
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <cstdint>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/types.h>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __DEPRECATED // Dodge GCC warnings
|
||||
# undef __DEPRECATED
|
||||
# include <strstream>
|
||||
@ -29,8 +34,6 @@
|
||||
#else
|
||||
# include <strstream>
|
||||
#endif
|
||||
#include <vector>
|
||||
#include <libaegisub/types.h>
|
||||
|
||||
// These macros below aren't a perm solution, it will depend on how annoying they are through
|
||||
// actual usage, and also depends on msvc support.
|
||||
@ -144,7 +147,7 @@ class JsonEmitter : public Emitter {
|
||||
agi_timeval time_start;
|
||||
|
||||
/// Directory to write the log file in
|
||||
std::string directory;
|
||||
agi::fs::path directory;
|
||||
|
||||
/// Parent sink to get messages from
|
||||
const agi::log::LogSink *log_sink;
|
||||
@ -152,7 +155,7 @@ public:
|
||||
/// Constructor
|
||||
/// @param directory Directory to write the log file in
|
||||
/// @param log_sink Parent sink to get messages from
|
||||
JsonEmitter(std::string const& directory, const agi::log::LogSink *log_sink);
|
||||
JsonEmitter(agi::fs::path const& directory, const agi::log::LogSink *log_sink);
|
||||
/// Destructor
|
||||
~JsonEmitter();
|
||||
|
||||
|
@ -16,12 +16,13 @@
|
||||
/// @brief Public interface for MRU (Most Recently Used) lists.
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include <libaegisub/exception.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
namespace json {
|
||||
class UnknownElement;
|
||||
@ -49,11 +50,11 @@ DEFINE_SIMPLE_EXCEPTION_NOINNER(MRUErrorIndexOutOfRange, MRUError, "mru/invalid"
|
||||
class MRUManager {
|
||||
public:
|
||||
/// @brief Map for time->value pairs.
|
||||
typedef std::list<std::string> MRUListMap;
|
||||
typedef std::list<agi::fs::path> MRUListMap;
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param config File to load MRU values from
|
||||
MRUManager(std::string const& config, std::string const& default_config, agi::Options *options = 0);
|
||||
MRUManager(agi::fs::path const& config, std::string const& default_config, agi::Options *options = 0);
|
||||
|
||||
/// Destructor
|
||||
~MRUManager();
|
||||
@ -62,13 +63,13 @@ public:
|
||||
/// @param key List name
|
||||
/// @param entry Entry to add
|
||||
/// @exception MRUErrorInvalidKey thrown when an invalid key is used.
|
||||
void Add(std::string const& key, std::string const& entry);
|
||||
void Add(std::string const& key, agi::fs::path const& entry);
|
||||
|
||||
/// @brief Remove entry from the list.
|
||||
/// @param key List name
|
||||
/// @param entry Entry to add
|
||||
/// @exception MRUErrorInvalidKey thrown when an invalid key is used.
|
||||
void Remove(std::string const& key, std::string const& entry);
|
||||
void Remove(std::string const& key, agi::fs::path const& entry);
|
||||
|
||||
/// @brief Return list
|
||||
/// @param key List name
|
||||
@ -79,14 +80,14 @@ public:
|
||||
/// @param key List name
|
||||
/// @param entry 0-base position of entry
|
||||
/// @exception MRUErrorInvalidKey thrown when an invalid key is used.
|
||||
std::string const& GetEntry(std::string const& key, const size_t entry);
|
||||
agi::fs::path const& GetEntry(std::string const& key, const size_t entry);
|
||||
|
||||
/// Write MRU lists to disk.
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
/// Internal name of the config file, set during object construction.
|
||||
const std::string config_name;
|
||||
const agi::fs::path config_name;
|
||||
|
||||
/// User preferences object for maximum number of items to list
|
||||
agi::Options *const options;
|
||||
|
@ -18,10 +18,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
|
||||
#include <libaegisub/exception.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
namespace json {
|
||||
class UnknownElement;
|
||||
@ -60,7 +62,7 @@ private:
|
||||
OptionValueMap values;
|
||||
|
||||
/// User config (file that will be written to disk)
|
||||
const std::string config_file;
|
||||
const agi::fs::path config_file;
|
||||
|
||||
/// Settings.
|
||||
const OptionSetting setting;
|
||||
@ -74,7 +76,7 @@ public:
|
||||
/// @brief Constructor
|
||||
/// @param file User config that will be loaded from and written back to.
|
||||
/// @param default_config Default configuration.
|
||||
Options(const std::string &file, const std::string &default_config, const OptionSetting setting=NONE);
|
||||
Options(agi::fs::path const& file, const std::string &default_config, const OptionSetting setting=NONE);
|
||||
|
||||
/// Destructor
|
||||
~Options();
|
||||
|
81
aegisub/libaegisub/include/libaegisub/path.h
Normal file
81
aegisub/libaegisub/include/libaegisub/path.h
Normal 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);
|
||||
};
|
||||
|
||||
}
|
45
aegisub/libaegisub/include/libaegisub/split.h
Normal file
45
aegisub/libaegisub/include/libaegisub/split.h
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
@ -16,10 +16,11 @@
|
||||
/// @brief MyThes-compatible thesaurus implementation
|
||||
/// @ingroup libaegisub thesaurus
|
||||
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
#include "fs_fwd.h"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -31,9 +32,9 @@ class Thesaurus {
|
||||
/// Map of word -> byte position in the data file
|
||||
std::map<std::string, int> offsets;
|
||||
/// Read handle to the data file
|
||||
scoped_ptr<std::ifstream> dat;
|
||||
std::unique_ptr<std::istream> dat;
|
||||
/// Converter from the data file's charset to UTF-8
|
||||
scoped_ptr<charset::IconvWrapper> conv;
|
||||
std::unique_ptr<charset::IconvWrapper> conv;
|
||||
|
||||
public:
|
||||
/// A pair of a word and synonyms for that word
|
||||
@ -42,7 +43,7 @@ public:
|
||||
/// Constructor
|
||||
/// @param dat_path Path to data file
|
||||
/// @param idx_path Path to index file
|
||||
Thesaurus(std::string const& dat_path, std::string const& idx_path);
|
||||
Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path);
|
||||
~Thesaurus();
|
||||
|
||||
/// Look up synonyms for a word
|
||||
|
@ -16,57 +16,31 @@
|
||||
/// @brief Public interface for general utilities.
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <libaegisub/types.h>
|
||||
|
||||
struct tm;
|
||||
|
||||
namespace agi {
|
||||
namespace util {
|
||||
/// Whether the path is a file or directory.
|
||||
enum PathType {
|
||||
TypeFile, ///< File
|
||||
TypeDir ///< Directory
|
||||
};
|
||||
|
||||
/// Clamp `b` to the range [`a`,`c`]
|
||||
template<typename T> inline T mid(T a, T b, T c) { return std::max(a, std::min(b, c)); }
|
||||
|
||||
/// Get the parent directory of a path.
|
||||
/// @param path Path to process.
|
||||
const std::string DirName(const std::string& path);
|
||||
|
||||
/// Rename a file.
|
||||
/// @param from Source.
|
||||
/// @param to Destination.
|
||||
void Rename(const std::string& from, const std::string& to);
|
||||
|
||||
/// Delete a file
|
||||
/// @param path Path to file to delete
|
||||
/// @throws agi::FileNotAccessibleError if file exists but could not be deleted
|
||||
void Remove(std::string const& path);
|
||||
|
||||
/// Get time suitable for logging mechanisms.
|
||||
/// @param tv timeval
|
||||
void time_log(agi_timeval &tv);
|
||||
|
||||
/// Make all alphabetic characters lowercase.
|
||||
/// @param str Input string
|
||||
void str_lower(std::string &str);
|
||||
|
||||
/// Convert a string to Integer.
|
||||
/// @param str Input string
|
||||
int strtoi(std::string const& str);
|
||||
|
||||
bool try_parse(std::string const& str, double *out);
|
||||
bool try_parse(std::string const& str, int *out);
|
||||
|
||||
/// Check for amount of free space on a Path.
|
||||
/// @param path[in] Path to check
|
||||
/// @param type PathType (default is TypeDir)
|
||||
uint64_t freespace(std::string const& path, PathType type=TypeDir);
|
||||
/// strftime, but on std::string rather than a fixed buffer
|
||||
/// @param fmt strftime format string
|
||||
/// @param tmptr Time to format, or nullptr for current time
|
||||
/// @return The strftime-formatted string
|
||||
std::string strftime(const char *fmt, const tm *tmptr = nullptr);
|
||||
|
||||
struct delete_ptr {
|
||||
template<class T>
|
||||
|
@ -28,6 +28,8 @@
|
||||
/// When linking with this library, be sure to add '-framework CoreFoundation'
|
||||
/// to the GCC commandline.
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace agi {
|
||||
@ -91,3 +93,5 @@ std::string OSX_GetBundleAuxillaryExecutablePath(std::string const& executableNa
|
||||
void OSX_OpenLocation(std::string const& location);
|
||||
} // namespace io
|
||||
} // namespace agi
|
||||
|
||||
#endif
|
||||
|
@ -23,9 +23,6 @@
|
||||
|
||||
namespace agi {
|
||||
namespace util {
|
||||
|
||||
std::string ErrorString(DWORD error);
|
||||
|
||||
|
||||
std::string ErrorString(DWORD error);
|
||||
} // namespace util
|
||||
} // namespace agi
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <libaegisub/exception.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
namespace agi {
|
||||
/// Framerate handling.
|
||||
@ -92,7 +93,7 @@ public:
|
||||
/// not the same thing as CFR X. When timecodes are loaded from a file,
|
||||
/// mkvmerge-style rounding is applied, while setting a constant frame rate
|
||||
/// uses truncation.
|
||||
Framerate(std::string const& filename);
|
||||
Framerate(fs::path const& filename);
|
||||
|
||||
/// @brief CFR constructor
|
||||
/// @param fps Frames per second or 0 for unloaded
|
||||
@ -195,7 +196,7 @@ public:
|
||||
/// CFR, but saving CFR timecodes is a bit silly). Extra timecodes generated
|
||||
/// to hit length with v2 timecodes will monotonically increase but may not
|
||||
/// be otherwise sensible.
|
||||
void Save(std::string const& file, int length = -1) const;
|
||||
void Save(fs::path const& file, int length = -1) const;
|
||||
|
||||
/// Is this frame rate possibly variable?
|
||||
bool IsVFR() const {return timecodes.size() > 1; }
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
// Common C
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
|
@ -20,59 +20,30 @@
|
||||
|
||||
#include "libaegisub/access.h"
|
||||
|
||||
#include "libaegisub/fs.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libaegisub/util.h"
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace agi {
|
||||
namespace acs {
|
||||
|
||||
|
||||
void CheckFileRead(const std::string &file) {
|
||||
Check(file, acs::FileRead);
|
||||
}
|
||||
|
||||
|
||||
void CheckFileWrite(const std::string &file) {
|
||||
Check(file, acs::FileWrite);
|
||||
}
|
||||
|
||||
|
||||
void CheckDirRead(const std::string &dir) {
|
||||
Check(dir, acs::DirRead);
|
||||
}
|
||||
|
||||
|
||||
void CheckDirWrite(const std::string &dir) {
|
||||
Check(dir, acs::DirWrite);
|
||||
}
|
||||
|
||||
|
||||
void Check(const std::string &file, acs::Type type) {
|
||||
void Check(agi::fs::path const& file, acs::Type type) {
|
||||
struct stat file_stat;
|
||||
int file_status;
|
||||
|
||||
file_status = stat(file.c_str(), &file_stat);
|
||||
int file_status = stat(file.c_str(), &file_stat);
|
||||
|
||||
if (file_status != 0) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
throw FileNotFoundError("File or path not found.");
|
||||
break;
|
||||
|
||||
throw fs::FileNotFound(file);
|
||||
case EACCES:
|
||||
throw Read("Access Denied to file, path or path component.");
|
||||
break;
|
||||
|
||||
throw fs::ReadDenied(file);
|
||||
case EIO:
|
||||
throw Fatal("Fatal I/O error occurred.");
|
||||
break;
|
||||
throw fs::FileSystemUnknownError("Fatal I/O error in 'stat' on path: " + file.string());
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,24 +51,24 @@ void Check(const std::string &file, acs::Type type) {
|
||||
case FileRead:
|
||||
case FileWrite:
|
||||
if ((file_stat.st_mode & S_IFREG) == 0)
|
||||
throw NotAFile("Not a file.");
|
||||
throw fs::NotAFile(file);
|
||||
break;
|
||||
|
||||
case DirRead:
|
||||
case DirWrite:
|
||||
if ((file_stat.st_mode & S_IFDIR) == 0)
|
||||
throw NotADirectory("Not a directory.");
|
||||
throw fs::NotADirectory(file);
|
||||
break;
|
||||
}
|
||||
|
||||
file_status = access(file.c_str(), R_OK);
|
||||
if (file_status != 0)
|
||||
throw Read("File or directory is not readable.");
|
||||
throw fs::ReadDenied(file);
|
||||
|
||||
if (type == DirWrite || type == FileWrite) {
|
||||
file_status = access(file.c_str(), W_OK);
|
||||
if (file_status != 0)
|
||||
throw Write("File or directory is not writable.");
|
||||
throw fs::WriteDenied(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
92
aegisub/libaegisub/unix/fs.cpp
Normal file
92
aegisub/libaegisub/unix/fs.cpp
Normal 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() { }
|
||||
|
||||
} }
|
@ -44,8 +44,8 @@ void EmitSTDOUT::log(SinkMessage *sm) {
|
||||
sm->file,
|
||||
sm->func,
|
||||
sm->line,
|
||||
(int)sm->len,
|
||||
sm->message);
|
||||
(int)sm->message.size(),
|
||||
sm->message.c_str());
|
||||
if (!isatty(fileno(stdout)))
|
||||
fflush(stdout);
|
||||
}
|
||||
|
59
aegisub/libaegisub/unix/path.cpp
Normal file
59
aegisub/libaegisub/unix/path.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -20,72 +20,10 @@
|
||||
|
||||
#include "libaegisub/util.h"
|
||||
|
||||
#include "libaegisub/access.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/statvfs.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace agi {
|
||||
namespace util {
|
||||
|
||||
|
||||
const std::string DirName(const std::string& path) {
|
||||
if (path.find('/') == std::string::npos) {
|
||||
return ".";
|
||||
}
|
||||
|
||||
return path.substr(0, path.rfind("/")+1);
|
||||
}
|
||||
|
||||
void Rename(const std::string& from, const std::string& to) {
|
||||
acs::CheckFileWrite(from);
|
||||
|
||||
try {
|
||||
acs::CheckFileWrite(to);
|
||||
} catch (FileNotFoundError const&) {
|
||||
acs::CheckDirWrite(DirName(to));
|
||||
}
|
||||
|
||||
rename(from.c_str(), to.c_str());
|
||||
}
|
||||
|
||||
void Remove(std::string const& path) {
|
||||
if (!remove(path.c_str()) && errno != ENOENT)
|
||||
throw agi::FileNotAccessibleError("Can not remove file: " + path);
|
||||
}
|
||||
namespace agi { namespace util {
|
||||
|
||||
void time_log(timeval &tv) {
|
||||
gettimeofday(&tv, (struct timezone *)NULL);
|
||||
}
|
||||
|
||||
uint64_t freespace(std::string const& path, PathType type) {
|
||||
struct statvfs fs;
|
||||
std::string check(path);
|
||||
|
||||
if (type == TypeFile)
|
||||
check.assign(DirName(path));
|
||||
|
||||
acs::CheckDirRead(check);
|
||||
|
||||
if ((statvfs(check.c_str(), &fs)) == 0) {
|
||||
return (uint64_t)fs.f_bsize * fs.f_bavail;
|
||||
} else {
|
||||
/// @todo We need a collective set of exceptions for ENOTDIR, EIO etc.
|
||||
throw("Failed getting free space");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace io
|
||||
} // namespace agi
|
||||
} }
|
||||
|
@ -16,18 +16,19 @@
|
||||
/// @brief Windows access methods.
|
||||
/// @ingroup libaegisub windows
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <libaegisub/access.h>
|
||||
|
||||
#include <libaegisub/charset_conv_win.h>
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/util.h>
|
||||
#include <libaegisub/util_win.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace {
|
||||
bool check_permission(bool is_read, SECURITY_DESCRIPTOR *sd, HANDLE client_token) {
|
||||
DWORD access_check = is_read ? FILE_READ_DATA : FILE_APPEND_DATA | FILE_WRITE_DATA;
|
||||
@ -48,43 +49,23 @@ namespace {
|
||||
namespace agi {
|
||||
namespace acs {
|
||||
|
||||
void CheckFileRead(const std::string &file) {
|
||||
Check(file, acs::FileRead);
|
||||
}
|
||||
|
||||
void CheckFileWrite(const std::string &file) {
|
||||
Check(file, acs::FileWrite);
|
||||
}
|
||||
|
||||
void CheckDirRead(const std::string &dir) {
|
||||
Check(dir, acs::DirRead);
|
||||
}
|
||||
|
||||
void CheckDirWrite(const std::string &dir) {
|
||||
Check(dir, acs::DirWrite);
|
||||
}
|
||||
|
||||
/*
|
||||
This function is still a proof of concept, it's probably rife with bugs, below
|
||||
is a short (and incomplete) todo
|
||||
* "Basic" checks (Read/Write/File/Dir) checks for FAT32 filesystems which
|
||||
requires detecting the filesystem being used.
|
||||
*/
|
||||
void Check(const std::string &file, acs::Type type) {
|
||||
std::wstring wfile = agi::charset::ConvertW(file);
|
||||
|
||||
DWORD file_attr = GetFileAttributes(wfile.c_str());
|
||||
void Check(fs::path const& file, acs::Type type) {
|
||||
DWORD file_attr = GetFileAttributes(file.c_str());
|
||||
if ((file_attr & INVALID_FILE_ATTRIBUTES) == INVALID_FILE_ATTRIBUTES) {
|
||||
switch (GetLastError()) {
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
throw FileNotFoundError(file);
|
||||
|
||||
throw fs::FileNotFound(file);
|
||||
case ERROR_ACCESS_DENIED:
|
||||
throw Read("Access denied to file or path component");
|
||||
|
||||
throw fs::ReadDenied(file);
|
||||
default:
|
||||
throw Fatal("Fatal I/O error occurred.");
|
||||
throw fs::FileSystemUnknownError(str(boost::format("Unexpected error when getting attributes for \"%s\": %s") % file % util::ErrorString(GetLastError())));
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,25 +73,25 @@ void Check(const std::string &file, acs::Type type) {
|
||||
case FileRead:
|
||||
case FileWrite:
|
||||
if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
|
||||
throw NotAFile(file + " is not a file");
|
||||
throw fs::NotAFile(file);
|
||||
break;
|
||||
case DirRead:
|
||||
case DirWrite:
|
||||
if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
|
||||
throw NotADirectory(file + " is not a directory");
|
||||
throw fs::NotADirectory(file);
|
||||
break;
|
||||
}
|
||||
|
||||
SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
|
||||
DWORD len = 0;
|
||||
GetFileSecurity(wfile.c_str(), info, nullptr, 0, &len);
|
||||
GetFileSecurity(file.c_str(), info, nullptr, 0, &len);
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
LOG_W("acs/check") << "GetFileSecurity: fatal: " << util::ErrorString(GetLastError());
|
||||
|
||||
std::vector<uint8_t> sd_buff(len);
|
||||
SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)&sd_buff[0];
|
||||
|
||||
if (!GetFileSecurity(wfile.c_str(), info, sd, len, &len))
|
||||
if (!GetFileSecurity(file.c_str(), info, sd, len, &len))
|
||||
LOG_W("acs/check") << "GetFileSecurity failed: " << util::ErrorString(GetLastError());
|
||||
|
||||
ImpersonateSelf(SecurityImpersonation);
|
||||
@ -119,9 +100,9 @@ void Check(const std::string &file, acs::Type type) {
|
||||
LOG_W("acs/check") << "OpenThreadToken failed: " << util::ErrorString(GetLastError());
|
||||
|
||||
if (!check_permission(true, sd, client_token))
|
||||
throw Read("File or directory is not readable");
|
||||
throw fs::ReadDenied(file);
|
||||
if ((type == DirWrite || type == FileWrite) && !check_permission(false, sd, client_token))
|
||||
throw Write("File or directory is not writable");
|
||||
throw fs::WriteDenied(file);
|
||||
}
|
||||
|
||||
} // namespace Access
|
||||
|
@ -18,6 +18,18 @@
|
||||
|
||||
#include <libaegisub/charset_conv_win.h>
|
||||
|
||||
namespace {
|
||||
std::string from_w(agi::charset::IconvWrapper &w32Conv, std::wstring const& source) {
|
||||
std::string dest;
|
||||
size_t srcLen = source.size() * sizeof(wchar_t);
|
||||
const char* src = reinterpret_cast<const char *>(source.c_str());
|
||||
size_t len = w32Conv.RequiredBufferSize(src, srcLen);
|
||||
dest.resize(len);
|
||||
w32Conv.Convert(src, srcLen, &dest[0], len);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
namespace agi {
|
||||
namespace charset {
|
||||
|
||||
@ -33,14 +45,12 @@ std::wstring ConvertW(std::string const& source) {
|
||||
|
||||
std::string ConvertW(std::wstring const& source) {
|
||||
static IconvWrapper w32Conv("utf-16le", "utf-8", false);
|
||||
return from_w(w32Conv, source);
|
||||
}
|
||||
|
||||
std::string dest;
|
||||
size_t srcLen = source.size() * sizeof(wchar_t);
|
||||
const char* src = reinterpret_cast<const char *>(source.c_str());
|
||||
size_t len = w32Conv.RequiredBufferSize(src, srcLen);
|
||||
dest.resize(len);
|
||||
w32Conv.Convert(src, srcLen, &dest[0], len);
|
||||
return dest;
|
||||
std::string ConvertLocal(std::wstring const& source) {
|
||||
static IconvWrapper w32Conv("utf-16le", "char", false);
|
||||
return from_w(w32Conv, source);
|
||||
}
|
||||
|
||||
}
|
||||
|
99
aegisub/libaegisub/windows/fs.cpp
Normal file
99
aegisub/libaegisub/windows/fs.cpp
Normal 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() { }
|
||||
|
||||
} }
|
57
aegisub/libaegisub/windows/path_win.cpp
Normal file
57
aegisub/libaegisub/windows/path_win.cpp
Normal 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"));
|
||||
}
|
||||
|
||||
}
|
@ -16,55 +16,18 @@
|
||||
/// @brief Windows utility methods.
|
||||
/// @ingroup libaegisub windows
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "libaegisub/util_win.h"
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "libaegisub/access.h"
|
||||
#include "libaegisub/charset_conv_win.h"
|
||||
#include "libaegisub/types.h"
|
||||
#include "libaegisub/util.h"
|
||||
#include "libaegisub/util_win.h"
|
||||
|
||||
namespace agi {
|
||||
namespace util {
|
||||
|
||||
using agi::charset::ConvertW;
|
||||
|
||||
const std::string DirName(const std::string& path) {
|
||||
std::string::size_type pos = path.rfind('/');
|
||||
|
||||
if (pos == std::string::npos) pos = path.rfind('\\');
|
||||
if (pos == std::string::npos) return ".";
|
||||
|
||||
return path.substr(0, pos+1);
|
||||
}
|
||||
|
||||
void Rename(const std::string& from, const std::string& to) {
|
||||
acs::CheckFileWrite(from);
|
||||
|
||||
try {
|
||||
acs::CheckFileWrite(to);
|
||||
} catch (FileNotFoundError const&) {
|
||||
acs::CheckDirWrite(DirName(to));
|
||||
}
|
||||
|
||||
if (!MoveFileEx(ConvertW(from).c_str(), ConvertW(to).c_str(), MOVEFILE_REPLACE_EXISTING))
|
||||
throw agi::FileNotAccessibleError("Can not overwrite file: " + ErrorString(GetLastError()));
|
||||
}
|
||||
|
||||
void Remove(std::string const& path) {
|
||||
if (!DeleteFile(ConvertW(path).c_str())) {
|
||||
DWORD err = GetLastError();
|
||||
if (err != ERROR_FILE_NOT_FOUND)
|
||||
throw agi::FileNotAccessibleError("Can not remove file: " + ErrorString(err));
|
||||
}
|
||||
}
|
||||
|
||||
std::string ErrorString(DWORD error) {
|
||||
LPWSTR lpstr = nullptr;
|
||||
|
||||
@ -116,19 +79,5 @@ void time_log(agi_timeval &tv) {
|
||||
tv.tv_usec = (long)(tmpres % 1000000UL);
|
||||
}
|
||||
|
||||
uint64_t freespace(std::string const& path, PathType type) {
|
||||
if (type == TypeFile)
|
||||
return freespace(DirName(path));
|
||||
|
||||
ULARGE_INTEGER bytes_available;
|
||||
if (GetDiskFreeSpaceEx(ConvertW(path).c_str(), &bytes_available, 0, 0))
|
||||
return bytes_available.QuadPart;
|
||||
|
||||
acs::CheckDirRead(path);
|
||||
|
||||
/// @todo GetLastError -> Exception mapping
|
||||
throw "Unknown error getting free space";
|
||||
}
|
||||
|
||||
} // namespace io
|
||||
} // namespace agi
|
||||
|
@ -157,7 +157,6 @@ SRC += \
|
||||
auto4_base.cpp \
|
||||
avisynth_wrap.cpp \
|
||||
base_grid.cpp \
|
||||
charset_conv.cpp \
|
||||
charset_detect.cpp \
|
||||
colorspace.cpp \
|
||||
colour_button.cpp \
|
||||
|
@ -36,18 +36,16 @@
|
||||
|
||||
#include "aegisublocale.h"
|
||||
|
||||
#include "standard_paths.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <clocale>
|
||||
#include <functional>
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/choicdlg.h> // Keep this last so wxUSE_CHOICEDLG is set.
|
||||
|
||||
#include "standard_paths.h"
|
||||
|
||||
#ifndef AEGISUB_CATALOG
|
||||
#define AEGISUB_CATALOG "aegisub"
|
||||
#endif
|
||||
@ -56,7 +54,7 @@ wxTranslations *AegisubLocale::GetTranslations() {
|
||||
wxTranslations *translations = wxTranslations::Get();
|
||||
if (!translations) {
|
||||
wxTranslations::Set(translations = new wxTranslations);
|
||||
wxFileTranslationsLoader::AddCatalogLookupPathPrefix(StandardPaths::DecodePath("?data/locale/"));
|
||||
wxFileTranslationsLoader::AddCatalogLookupPathPrefix(StandardPaths::DecodePath("?data/locale/").wstring());
|
||||
}
|
||||
return translations;
|
||||
}
|
||||
|
@ -74,7 +74,6 @@
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
#include <boost/range/adaptor/indirected.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/range/algorithm_ext.hpp>
|
||||
|
||||
// wxWidgets headers
|
||||
#include <wx/wxprec.h> // Leave this first.
|
||||
|
@ -34,26 +34,33 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <wx/filename.h>
|
||||
|
||||
#include <istream>
|
||||
|
||||
#include "ass_attachment.h"
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#include <libaegisub/io.h>
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
|
||||
AssAttachment::AssAttachment(wxString const& name, AssEntryGroup group)
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <fstream>
|
||||
|
||||
AssAttachment::AssAttachment(std::string const& name, AssEntryGroup group)
|
||||
: data(new std::vector<char>)
|
||||
, filename(name)
|
||||
, group(group)
|
||||
{
|
||||
wxFileName fname(filename);
|
||||
wxString ext = fname.GetExt().Lower();
|
||||
if (ext == "ttf")
|
||||
filename = fname.GetName() + "_0." + ext;
|
||||
}
|
||||
|
||||
AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group)
|
||||
: data(new std::vector<char>)
|
||||
, filename(name.filename().string())
|
||||
, group(group)
|
||||
{
|
||||
if (boost::iends_with(filename, ".ttf"))
|
||||
filename = filename.substr(0, filename.size() - 4) + "_0" + filename.substr(filename.size() - 4);
|
||||
|
||||
std::unique_ptr<std::istream> file(agi::io::Open(name, true));
|
||||
file->seekg(0, std::ios::end);
|
||||
data->resize(file->tellg());
|
||||
file->seekg(0, std::ios::beg);
|
||||
file->read(&(*data)[0], data->size());
|
||||
}
|
||||
|
||||
AssEntry *AssAttachment::Clone() const {
|
||||
@ -62,46 +69,27 @@ AssEntry *AssAttachment::Clone() const {
|
||||
return clone;
|
||||
}
|
||||
|
||||
const wxString AssAttachment::GetEntryData() const {
|
||||
size_t pos = 0;
|
||||
const std::string AssAttachment::GetEntryData() const {
|
||||
size_t size = data->size();
|
||||
size_t written = 0;
|
||||
unsigned char src[3];
|
||||
unsigned char dst[4];
|
||||
|
||||
// Write header
|
||||
wxString entryData = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename + "\r\n";
|
||||
std::string entryData = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename + "\r\n";
|
||||
entryData.reserve(size * 4 / 3 + size / 80 * 2 + entryData.size() + 2);
|
||||
|
||||
// Read three bytes
|
||||
while (pos < size) {
|
||||
// Number to read
|
||||
size_t read = size - pos;
|
||||
if (read > 3) read = 3;
|
||||
for (size_t pos = 0; pos < size; pos += 3) {
|
||||
unsigned char src[3] = { '\0', '\0', '\0' };
|
||||
memcpy(src, &(*data)[pos], std::min<size_t>(3u, size - pos));
|
||||
|
||||
// Read source
|
||||
src[0] = (*data)[pos];
|
||||
if (read >= 2) src[1] = (*data)[pos+1];
|
||||
else src[1] = 0;
|
||||
if (read == 3) src[2] = (*data)[pos+2];
|
||||
else src[2] = 0;
|
||||
pos += read;
|
||||
|
||||
// Codify
|
||||
unsigned char dst[4];
|
||||
dst[0] = src[0] >> 2;
|
||||
dst[1] = ((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4);
|
||||
dst[2] = ((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6);
|
||||
dst[3] = src[2] & 0x3F;
|
||||
|
||||
// Number to write
|
||||
size_t toWrite = read+1;
|
||||
for (size_t i = 0; i < std::min<size_t>(size - pos + 1, 4u); ++i) {
|
||||
entryData += dst[i] + 33;
|
||||
|
||||
// Convert to text
|
||||
for (size_t i=0;i<toWrite;i++) {
|
||||
entryData += dst[i]+33;
|
||||
written++;
|
||||
|
||||
// Line break
|
||||
if (written == 80 && pos < size) {
|
||||
if (++written == 80 && pos + 3 < size) {
|
||||
written = 0;
|
||||
entryData += "\r\n";
|
||||
}
|
||||
@ -111,39 +99,28 @@ const wxString AssAttachment::GetEntryData() const {
|
||||
return entryData;
|
||||
}
|
||||
|
||||
void AssAttachment::Extract(wxString const& filename) const {
|
||||
agi::io::Save(from_wx(filename), true).Get().write(&(*data)[0], data->size());
|
||||
void AssAttachment::Extract(agi::fs::path const& filename) const {
|
||||
agi::io::Save(filename, true).Get().write(&(*data)[0], data->size());
|
||||
}
|
||||
|
||||
void AssAttachment::Import(wxString const& filename) {
|
||||
agi::scoped_ptr<std::istream> file(agi::io::Open(from_wx(filename), true));
|
||||
file->seekg(0, std::ios::end);
|
||||
data->resize(file->tellg());
|
||||
file->seekg(0, std::ios::beg);
|
||||
file->read(&(*data)[0], data->size());
|
||||
}
|
||||
|
||||
wxString AssAttachment::GetFileName(bool raw) const {
|
||||
if (raw || filename.Right(4).Lower() != ".ttf") return filename;
|
||||
std::string AssAttachment::GetFileName(bool raw) const {
|
||||
if (raw || !boost::iends_with(filename, ".ttf")) return filename;
|
||||
|
||||
// Remove stuff after last underscore if it's a font
|
||||
wxString::size_type last_under = filename.rfind('_');
|
||||
if (last_under == wxString::npos)
|
||||
std::string::size_type last_under = filename.rfind('_');
|
||||
if (last_under == std::string::npos)
|
||||
return filename;
|
||||
|
||||
return filename.Left(last_under) + ".ttf";
|
||||
return filename.substr(0, last_under) + ".ttf";
|
||||
}
|
||||
|
||||
void AssAttachment::Finish() {
|
||||
// Source and dest buffers
|
||||
unsigned char src[4];
|
||||
unsigned char dst[3];
|
||||
|
||||
data->reserve(buffer.size() * 3 / 4);
|
||||
|
||||
// Read buffer
|
||||
for(size_t pos = 0; pos + 1 < buffer.size(); ) {
|
||||
// Find characters left
|
||||
size_t read = std::min<size_t>(buffer.size() - pos, 4);
|
||||
|
||||
// Move 4 bytes from buffer to src
|
||||
@ -157,11 +134,9 @@ void AssAttachment::Finish() {
|
||||
dst[1] = ((src[1] & 0xF) << 4) | (src[2] >> 2);
|
||||
dst[2] = ((src[2] & 0x3) << 6) | (src[3]);
|
||||
|
||||
// Push into vector
|
||||
copy(dst, dst + read - 1, back_inserter(*data));
|
||||
}
|
||||
|
||||
// Clear buffer
|
||||
buffer.clear();
|
||||
buffer.Shrink();
|
||||
buffer.shrink_to_fit();
|
||||
}
|
||||
|
@ -32,21 +32,23 @@
|
||||
/// @ingroup subs_storage
|
||||
///
|
||||
|
||||
#include "ass_entry.h"
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "ass_entry.h"
|
||||
|
||||
/// @class AssAttachment
|
||||
class AssAttachment : public AssEntry {
|
||||
/// Decoded file data
|
||||
std::shared_ptr<std::vector<char>> data;
|
||||
|
||||
/// Encoded data which has been read from the script but not yet decoded
|
||||
wxString buffer;
|
||||
std::vector<char> buffer;
|
||||
|
||||
/// Name of the attached file, with SSA font mangling if it is a ttf
|
||||
wxString filename;
|
||||
std::string filename;
|
||||
|
||||
AssEntryGroup group;
|
||||
|
||||
@ -56,25 +58,22 @@ public:
|
||||
|
||||
/// Add a line of data (without newline) read from a subtitle file to the
|
||||
/// buffer waiting to be decoded
|
||||
void AddData(wxString const& data) { buffer += data; }
|
||||
void AddData(std::string const& data) { buffer.insert(buffer.end(), data.begin(), data.end()); }
|
||||
/// Decode all data passed with AddData
|
||||
void Finish();
|
||||
|
||||
/// Extract the contents of this attachment to a file
|
||||
/// @param filename Path to save the attachment to
|
||||
void Extract(wxString const& filename) const;
|
||||
|
||||
/// Import the contents of a file as an attachment
|
||||
/// @param filename Path to import
|
||||
void Import(wxString const& filename);
|
||||
void Extract(agi::fs::path const& filename) const;
|
||||
|
||||
/// Get the name of the attached file
|
||||
/// @param raw If false, remove the SSA filename mangling
|
||||
wxString GetFileName(bool raw=false) const;
|
||||
std::string GetFileName(bool raw=false) const;
|
||||
|
||||
const wxString GetEntryData() const override;
|
||||
const std::string GetEntryData() const override;
|
||||
AssEntryGroup Group() const override { return group; }
|
||||
AssEntry *Clone() const override;
|
||||
|
||||
AssAttachment(wxString const& name, AssEntryGroup group);
|
||||
AssAttachment(std::string const& name, AssEntryGroup group);
|
||||
AssAttachment(agi::fs::path const& name, AssEntryGroup group);
|
||||
};
|
||||
|
@ -34,27 +34,24 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "ass_dialogue.h"
|
||||
#include "compat.h"
|
||||
#include "subtitle_format.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/of_type_adaptor.h>
|
||||
#include <libaegisub/split.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
|
||||
#include <wx/regex.h>
|
||||
#include <wx/tokenzr.h>
|
||||
#include <boost/spirit/include/karma_generate.hpp>
|
||||
#include <boost/spirit/include/karma_int.hpp>
|
||||
|
||||
using namespace boost::adaptors;
|
||||
|
||||
static int next_id = 0;
|
||||
|
||||
std::size_t hash_value(wxString const& s) {
|
||||
return wxStringHash()(s);
|
||||
}
|
||||
|
||||
AssDialogue::AssDialogue()
|
||||
: Id(++next_id)
|
||||
, Comment(false)
|
||||
@ -80,105 +77,91 @@ AssDialogue::AssDialogue(AssDialogue const& that)
|
||||
memmove(Margin, that.Margin, sizeof Margin);
|
||||
}
|
||||
|
||||
AssDialogue::AssDialogue(wxString const& data)
|
||||
AssDialogue::AssDialogue(std::string const& data)
|
||||
: Id(++next_id)
|
||||
{
|
||||
if (!Parse(data))
|
||||
throw SubtitleFormatParseError(from_wx("Failed parsing line: " + data), 0);
|
||||
Parse(data);
|
||||
}
|
||||
|
||||
AssDialogue::~AssDialogue () {
|
||||
}
|
||||
|
||||
bool AssDialogue::Parse(wxString const& rawData) {
|
||||
size_t pos = 0;
|
||||
wxString temp;
|
||||
class tokenizer {
|
||||
agi::StringRange str;
|
||||
boost::split_iterator<agi::StringRange::const_iterator> pos;
|
||||
|
||||
// Get type
|
||||
if (rawData.StartsWith("Dialogue:")) {
|
||||
public:
|
||||
tokenizer(agi::StringRange const& str) : str(str) , pos(agi::Split(str, ',')) { }
|
||||
|
||||
agi::StringRange next_tok() {
|
||||
if (pos.eof())
|
||||
throw SubtitleFormatParseError("Failed parsing line: " + std::string(str.begin(), str.end()), 0);
|
||||
return *pos++;
|
||||
}
|
||||
|
||||
std::string next_str() { return agi::str(next_tok()); }
|
||||
std::string next_str_trim() { return agi::str(boost::trim_copy(next_tok())); }
|
||||
};
|
||||
|
||||
void AssDialogue::Parse(std::string const& raw) {
|
||||
agi::StringRange str;
|
||||
if (boost::starts_with(raw, "Dialogue:")) {
|
||||
Comment = false;
|
||||
pos = 10;
|
||||
str = agi::StringRange(raw.begin() + 10, raw.end());
|
||||
}
|
||||
else if (rawData.StartsWith("Comment:")) {
|
||||
else if (boost::starts_with(raw, "Comment:")) {
|
||||
Comment = true;
|
||||
pos = 9;
|
||||
str = agi::StringRange(raw.begin() + 9, raw.end());
|
||||
}
|
||||
else return false;
|
||||
else
|
||||
throw SubtitleFormatParseError("Failed parsing line: " + raw, 0);
|
||||
|
||||
wxStringTokenizer tkn(rawData.Mid(pos),",",wxTOKEN_RET_EMPTY_ALL);
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
tokenizer tkn(str);
|
||||
|
||||
// Get first token and see if it has "Marked=" in it
|
||||
temp = tkn.GetNextToken().Trim(false).Trim(true);
|
||||
bool ssa = temp.Lower().StartsWith("marked=");
|
||||
auto tmp = tkn.next_str_trim();
|
||||
bool ssa = boost::istarts_with(tmp, "marked=");
|
||||
|
||||
// Get layer number
|
||||
if (ssa)
|
||||
Layer = 0;
|
||||
else {
|
||||
long templ;
|
||||
temp.ToLong(&templ);
|
||||
Layer = templ;
|
||||
}
|
||||
else
|
||||
Layer = boost::lexical_cast<int>(tmp);
|
||||
|
||||
// Get start time
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
Start = tkn.GetNextToken();
|
||||
|
||||
// Get end time
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
End = tkn.GetNextToken();
|
||||
|
||||
// Get style
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
Style = tkn.GetNextToken().Trim(true).Trim(false);
|
||||
|
||||
// Get actor
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
Actor = tkn.GetNextToken().Trim(true).Trim(false);
|
||||
|
||||
// Get margins
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
SetMarginString(tkn.GetNextToken().Trim(false).Trim(true), i);
|
||||
}
|
||||
|
||||
if (!tkn.HasMoreTokens()) return false;
|
||||
Effect = tkn.GetNextToken().Trim(true).Trim(false);
|
||||
|
||||
// Get text
|
||||
Text = rawData.Mid(pos + tkn.GetPosition());
|
||||
|
||||
return true;
|
||||
Start = tkn.next_str_trim();
|
||||
End = tkn.next_str_trim();
|
||||
Style = tkn.next_str_trim();
|
||||
Actor = tkn.next_str_trim();
|
||||
for (int& margin : Margin)
|
||||
margin = mid(0, boost::lexical_cast<int>(tkn.next_str()), 9999);
|
||||
Effect = tkn.next_str_trim();
|
||||
Text = std::string(tkn.next_tok().begin(), str.end());
|
||||
}
|
||||
|
||||
static void append_int(wxString &str, int v) {
|
||||
str += std::to_wstring(v);
|
||||
void append_int(std::string &str, int v) {
|
||||
boost::spirit::karma::generate(back_inserter(str), boost::spirit::karma::int_, v);
|
||||
str += ',';
|
||||
}
|
||||
|
||||
static void append_str(wxString &out, wxString const& str) {
|
||||
void append_str(std::string &out, std::string const& str) {
|
||||
out += str;
|
||||
out += ',';
|
||||
}
|
||||
|
||||
static void append_unsafe_str(wxString &out, wxString const& str) {
|
||||
void append_unsafe_str(std::string &out, std::string const& str) {
|
||||
if (str.find(',') == str.npos)
|
||||
out += str;
|
||||
else {
|
||||
wxString c = str;
|
||||
c.Replace(wxS(","), wxS(";"));
|
||||
out += c;
|
||||
}
|
||||
else
|
||||
out += boost::replace_all_copy(str, ",", ";");
|
||||
out += ',';
|
||||
}
|
||||
|
||||
wxString AssDialogue::GetData(bool ssa) const {
|
||||
wxString str = Comment ? wxS("Comment: ") : wxS("Dialogue: ");
|
||||
std::string AssDialogue::GetData(bool ssa) const {
|
||||
std::string str = Comment ? "Comment: " : "Dialogue: ";
|
||||
str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size());
|
||||
|
||||
if (ssa)
|
||||
append_str(str, wxS("Marked=0"));
|
||||
append_str(str, "Marked=0");
|
||||
else
|
||||
append_int(str, Layer);
|
||||
append_str(str, Start.GetAssFormated());
|
||||
@ -190,20 +173,19 @@ wxString AssDialogue::GetData(bool ssa) const {
|
||||
append_unsafe_str(str, Effect);
|
||||
str += Text.get();
|
||||
|
||||
// Make sure that final has no line breaks
|
||||
if (str.find('\n') != str.npos || str.find('\r') != str.npos) {
|
||||
str.Replace("\n", "");
|
||||
str.Replace("\r", "");
|
||||
boost::replace_all(str, "\n", "");
|
||||
boost::replace_all(str, "\r", "");
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
const wxString AssDialogue::GetEntryData() const {
|
||||
const std::string AssDialogue::GetEntryData() const {
|
||||
return GetData(false);
|
||||
}
|
||||
|
||||
wxString AssDialogue::GetSSAText() const {
|
||||
std::string AssDialogue::GetSSAText() const {
|
||||
return GetData(true);
|
||||
}
|
||||
|
||||
@ -217,7 +199,7 @@ std::auto_ptr<boost::ptr_vector<AssDialogueBlock>> AssDialogue::ParseTags() cons
|
||||
}
|
||||
|
||||
int drawingLevel = 0;
|
||||
std::string text(from_wx(Text.get()));
|
||||
std::string const& text(Text.get());
|
||||
|
||||
for (size_t len = text.size(), cur = 0; cur < len; ) {
|
||||
// Overrides block
|
||||
@ -284,32 +266,17 @@ void AssDialogue::StripTags() {
|
||||
static std::string get_text(AssDialogueBlock &d) { return d.GetText(); }
|
||||
void AssDialogue::UpdateText(boost::ptr_vector<AssDialogueBlock>& blocks) {
|
||||
if (blocks.empty()) return;
|
||||
Text = to_wx(join(blocks | transformed(get_text), ""));
|
||||
Text = join(blocks | transformed(get_text), "");
|
||||
}
|
||||
|
||||
void AssDialogue::SetMarginString(wxString const& origvalue, int which) {
|
||||
void AssDialogue::SetMarginString(std::string const& origvalue, int which) {
|
||||
if (which < 0 || which > 2) throw InvalidMarginIdError();
|
||||
|
||||
// Make it numeric
|
||||
wxString strvalue = origvalue;
|
||||
if (!strvalue.IsNumber()) {
|
||||
strvalue.clear();
|
||||
for (size_t i = 0; i < origvalue.Length(); ++i) {
|
||||
if (origvalue.Mid(i, 1).IsNumber()) {
|
||||
strvalue += origvalue.Mid(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get value
|
||||
long value = 0;
|
||||
strvalue.ToLong(&value);
|
||||
Margin[which] = mid<int>(0, value, 9999);
|
||||
Margin[which] = mid<int>(0, atoi(origvalue.c_str()), 9999);
|
||||
}
|
||||
|
||||
wxString AssDialogue::GetMarginString(int which) const {
|
||||
std::string AssDialogue::GetMarginString(int which) const {
|
||||
if (which < 0 || which > 2) throw InvalidMarginIdError();
|
||||
return wxString::Format("%d", Margin[which]);
|
||||
return std::to_string(Margin[which]);
|
||||
}
|
||||
|
||||
bool AssDialogue::CollidesWith(const AssDialogue *target) const {
|
||||
@ -318,10 +285,9 @@ bool AssDialogue::CollidesWith(const AssDialogue *target) const {
|
||||
}
|
||||
|
||||
static std::string get_text_p(AssDialogueBlock *d) { return d->GetText(); }
|
||||
wxString AssDialogue::GetStrippedText() const {
|
||||
wxString ret;
|
||||
std::string AssDialogue::GetStrippedText() const {
|
||||
boost::ptr_vector<AssDialogueBlock> blocks(ParseTags());
|
||||
return to_wx(join(blocks | agi::of_type<AssDialogueBlockPlain>() | transformed(get_text_p), ""));
|
||||
return join(blocks | agi::of_type<AssDialogueBlockPlain>() | transformed(get_text_p), "");
|
||||
}
|
||||
|
||||
AssEntry *AssDialogue::Clone() const {
|
||||
@ -336,10 +302,9 @@ void AssDialogueBlockDrawing::TransformCoords(int mx, int my, double x, double y
|
||||
bool is_x = true;
|
||||
std::string final;
|
||||
|
||||
boost::char_separator<char> sep(" ");
|
||||
for (auto const& cur : boost::tokenizer<boost::char_separator<char>>(text, sep)) {
|
||||
for (auto const& cur : agi::Split(text, ' ')) {
|
||||
if (std::all_of(begin(cur), end(cur), isdigit)) {
|
||||
int val = boost::lexical_cast<int>(cur);
|
||||
int val = boost::lexical_cast<int>(agi::str(cur));
|
||||
if (is_x)
|
||||
val = (int)((val + mx) * x + .5);
|
||||
else
|
||||
|
@ -50,8 +50,6 @@ enum AssBlockType {
|
||||
BLOCK_DRAWING
|
||||
};
|
||||
|
||||
std::size_t hash_value(wxString const& s);
|
||||
|
||||
/// @class AssDialogueBlock
|
||||
/// @brief AssDialogue Blocks
|
||||
///
|
||||
@ -126,7 +124,11 @@ public:
|
||||
};
|
||||
|
||||
class AssDialogue : public AssEntry {
|
||||
wxString GetData(bool ssa) const;
|
||||
std::string GetData(bool ssa) const;
|
||||
|
||||
/// @brief Parse raw ASS data into everything else
|
||||
/// @param data ASS line
|
||||
void Parse(std::string const& data);
|
||||
public:
|
||||
/// Unique ID of this line. Copies of the line for Undo/Redo purposes
|
||||
/// preserve the unique ID, so that the equivalent lines can be found in
|
||||
@ -144,21 +146,16 @@ public:
|
||||
/// Ending time
|
||||
AssTime End;
|
||||
/// Style name
|
||||
boost::flyweight<wxString> Style;
|
||||
boost::flyweight<std::string> Style;
|
||||
/// Actor name
|
||||
boost::flyweight<wxString> Actor;
|
||||
boost::flyweight<std::string> Actor;
|
||||
/// Effect name
|
||||
boost::flyweight<wxString> Effect;
|
||||
boost::flyweight<std::string> Effect;
|
||||
/// Raw text data
|
||||
boost::flyweight<wxString> Text;
|
||||
boost::flyweight<std::string> Text;
|
||||
|
||||
AssEntryGroup Group() const override { return ENTRY_DIALOGUE; }
|
||||
|
||||
/// @brief Parse raw ASS data into everything else
|
||||
/// @param data ASS line
|
||||
/// @return Did it successfully parse?
|
||||
bool Parse(wxString const& data);
|
||||
|
||||
/// Parse text as ASS and return block information
|
||||
std::auto_ptr<boost::ptr_vector<AssDialogueBlock>> ParseTags() const;
|
||||
|
||||
@ -166,23 +163,23 @@ public:
|
||||
void StripTags();
|
||||
/// Strip a specific ASS tag from the text
|
||||
/// Get text without tags
|
||||
wxString GetStrippedText() const;
|
||||
std::string GetStrippedText() const;
|
||||
|
||||
/// Update the text of the line from parsed blocks
|
||||
void UpdateText(boost::ptr_vector<AssDialogueBlock>& blocks);
|
||||
const wxString GetEntryData() const override;
|
||||
const std::string GetEntryData() const override;
|
||||
|
||||
template<int which>
|
||||
void SetMarginString(wxString const& value) { SetMarginString(value, which);}
|
||||
void SetMarginString(std::string const& value) { SetMarginString(value, which);}
|
||||
/// @brief Set a margin
|
||||
/// @param value New value of the margin
|
||||
/// @param which 0 = left, 1 = right, 2 = vertical
|
||||
void SetMarginString(wxString const& value, int which);
|
||||
void SetMarginString(std::string const& value, int which);
|
||||
/// @brief Get a margin
|
||||
/// @param which 0 = left, 1 = right, 2 = vertical
|
||||
wxString GetMarginString(int which) const;
|
||||
std::string GetMarginString(int which) const;
|
||||
/// Get the line as SSA rather than ASS
|
||||
wxString GetSSAText() const override;
|
||||
std::string GetSSAText() const override;
|
||||
/// Does this line collide with the passed line?
|
||||
bool CollidesWith(const AssDialogue *target) const;
|
||||
|
||||
@ -190,7 +187,7 @@ public:
|
||||
|
||||
AssDialogue();
|
||||
AssDialogue(AssDialogue const&);
|
||||
AssDialogue(wxString const& data);
|
||||
AssDialogue(std::string const& data);
|
||||
~AssDialogue();
|
||||
};
|
||||
|
||||
|
@ -23,8 +23,8 @@
|
||||
|
||||
#include "ass_entry.h"
|
||||
|
||||
wxString const& AssEntry::GroupHeader(bool ssa) const {
|
||||
static wxString ass_headers[] = {
|
||||
std::string const& AssEntry::GroupHeader(bool ssa) const {
|
||||
static std::string ass_headers[] = {
|
||||
"[Script Info]",
|
||||
"[V4+ Styles]",
|
||||
"[Fonts]",
|
||||
@ -33,7 +33,7 @@ wxString const& AssEntry::GroupHeader(bool ssa) const {
|
||||
""
|
||||
};
|
||||
|
||||
static wxString ssa_headers[] = {
|
||||
static std::string ssa_headers[] = {
|
||||
"[Script Info]",
|
||||
"[V4 Styles]",
|
||||
"[Fonts]",
|
||||
|
@ -34,9 +34,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <boost/intrusive/list_hook.hpp>
|
||||
#include <string>
|
||||
|
||||
enum AssEntryGroup {
|
||||
ENTRY_INFO = 0,
|
||||
@ -58,11 +57,11 @@ public:
|
||||
virtual AssEntryGroup Group() const=0;
|
||||
|
||||
/// ASS or SSA Section header for this entry's group
|
||||
wxString const& GroupHeader(bool ssa=false) const;
|
||||
std::string const& GroupHeader(bool ssa=false) const;
|
||||
|
||||
/// @brief Get this line's raw entry data in ASS format
|
||||
virtual const wxString GetEntryData() const=0;
|
||||
virtual const std::string GetEntryData() const=0;
|
||||
|
||||
/// Get this line in SSA format
|
||||
virtual wxString GetSSAText() const { return GetEntryData(); }
|
||||
virtual std::string GetSSAText() const { return GetEntryData(); }
|
||||
};
|
||||
|
@ -34,12 +34,19 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "ass_export_filter.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
AssExportFilter::AssExportFilter(wxString const& name, wxString const& description, int priority)
|
||||
#include <algorithm>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
static FilterList& filters() {
|
||||
static FilterList instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
AssExportFilter::AssExportFilter(std::string const& name, std::string const& description, int priority)
|
||||
: name(name)
|
||||
, priority(priority)
|
||||
, description(description)
|
||||
@ -47,52 +54,44 @@ AssExportFilter::AssExportFilter(wxString const& name, wxString const& descripti
|
||||
}
|
||||
|
||||
void AssExportFilterChain::Register(AssExportFilter *filter) {
|
||||
// Remove pipes from name
|
||||
filter->name.Replace("|", "");
|
||||
|
||||
int filter_copy = 1;
|
||||
wxString name = filter->name;
|
||||
std::string name = filter->name;
|
||||
// Find a unique name
|
||||
while (GetFilter(name))
|
||||
name = wxString::Format("%s (%d)", filter->name, filter_copy++);
|
||||
name = str(boost::format("%s (%d)") % filter->name % filter_copy++);
|
||||
|
||||
filter->name = name;
|
||||
|
||||
// Look for place to insert
|
||||
FilterList::iterator begin = filters()->begin();
|
||||
FilterList::iterator end = filters()->end();
|
||||
auto begin(filters().begin()), end(filters().end());
|
||||
while (begin != end && (*begin)->priority >= filter->priority) ++begin;
|
||||
filters()->insert(begin, filter);
|
||||
filters().insert(begin, filter);
|
||||
}
|
||||
|
||||
void AssExportFilterChain::Unregister(AssExportFilter *filter) {
|
||||
auto it = remove(begin(*filters()), end(*filters()), filter);
|
||||
if (it == end(*filters()))
|
||||
auto it = remove(begin(filters()), end(filters()), filter);
|
||||
if (it == end(filters()))
|
||||
throw wxString::Format("Unregister export filter: name \"%s\" is not registered.", filter->name);
|
||||
|
||||
filters()->pop_back();
|
||||
filters().pop_back();
|
||||
}
|
||||
|
||||
FilterList *AssExportFilterChain::filters() {
|
||||
static FilterList instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
const FilterList *AssExportFilterChain::GetFilterList() {
|
||||
return filters();
|
||||
return &filters();
|
||||
}
|
||||
|
||||
void AssExportFilterChain::Clear() {
|
||||
while (filters()->size() > 0) {
|
||||
AssExportFilter *f = filters()->back();
|
||||
while (filters().size() > 0) {
|
||||
AssExportFilter *f = filters().back();
|
||||
delete f;
|
||||
if (filters()->size() && filters()->back() == f)
|
||||
filters()->pop_back();
|
||||
if (filters().size() && filters().back() == f)
|
||||
filters().pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
AssExportFilter *AssExportFilterChain::GetFilter(wxString const& name) {
|
||||
for (auto filter : *filters()) {
|
||||
AssExportFilter *AssExportFilterChain::GetFilter(std::string const& name) {
|
||||
for (auto filter : filters()) {
|
||||
if (filter->name == name)
|
||||
return filter;
|
||||
}
|
||||
|
@ -35,20 +35,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/window.h>
|
||||
|
||||
class AssFile;
|
||||
class AssExportFilter;
|
||||
class wxWindow;
|
||||
|
||||
namespace agi { struct Context; }
|
||||
|
||||
typedef std::vector<AssExportFilter*> FilterList;
|
||||
|
||||
class AssExportFilterChain {
|
||||
static FilterList *filters();
|
||||
public:
|
||||
/// Register an export filter
|
||||
static void Register(AssExportFilter *filter);
|
||||
@ -57,7 +55,7 @@ public:
|
||||
/// Unregister and delete all export filters
|
||||
static void Clear();
|
||||
/// Get a filter by name or nullptr if it doesn't exist
|
||||
static AssExportFilter *GetFilter(wxString const& name);
|
||||
static AssExportFilter *GetFilter(std::string const& name);
|
||||
|
||||
/// Get the list of registered filters
|
||||
static const FilterList *GetFilterList();
|
||||
@ -69,20 +67,20 @@ class AssExportFilter {
|
||||
friend class AssExportFilterChain;
|
||||
|
||||
/// This filter's name
|
||||
wxString name;
|
||||
std::string name;
|
||||
|
||||
/// Higher priority = run earlier
|
||||
int priority;
|
||||
|
||||
/// User-visible description of this filter
|
||||
wxString description;
|
||||
std::string description;
|
||||
|
||||
public:
|
||||
AssExportFilter(wxString const& name, wxString const& description, int priority = 0);
|
||||
AssExportFilter(std::string const& name, std::string const& description, int priority = 0);
|
||||
virtual ~AssExportFilter() { };
|
||||
|
||||
wxString const& GetName() const { return name; }
|
||||
wxString const& GetDescription() const { return description; }
|
||||
std::string const& GetName() const { return name; }
|
||||
std::string const& GetDescription() const { return description; }
|
||||
|
||||
/// Process subtitles
|
||||
/// @param subs Subtitles to process
|
||||
|
@ -34,14 +34,16 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ass_export_filter.h"
|
||||
#include "ass_exporter.h"
|
||||
|
||||
#include "ass_export_filter.h"
|
||||
#include "ass_file.h"
|
||||
#include "compat.h"
|
||||
#include "include/aegisub/context.h"
|
||||
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <wx/sizer.h>
|
||||
|
||||
static inline FilterList::const_iterator filter_list_begin() {
|
||||
return AssExportFilterChain::GetFilterList()->begin();
|
||||
@ -62,7 +64,7 @@ void AssExporter::DrawSettings(wxWindow *parent, wxSizer *target_sizer) {
|
||||
for (auto filter : *AssExportFilterChain::GetFilterList()) {
|
||||
// Make sure to construct static box sizer first, so it won't overlap
|
||||
// the controls on wxMac.
|
||||
wxSizer *box = new wxStaticBoxSizer(wxVERTICAL, parent, filter->GetName());
|
||||
wxSizer *box = new wxStaticBoxSizer(wxVERTICAL, parent, to_wx(filter->GetName()));
|
||||
wxWindow *window = filter->GetConfigDialogWindow(parent, c);
|
||||
if (window) {
|
||||
box->Add(window, 0, wxEXPAND, 0);
|
||||
@ -76,18 +78,18 @@ void AssExporter::DrawSettings(wxWindow *parent, wxSizer *target_sizer) {
|
||||
}
|
||||
}
|
||||
|
||||
void AssExporter::AddFilter(wxString const& name) {
|
||||
void AssExporter::AddFilter(std::string const& name) {
|
||||
AssExportFilter *filter = AssExportFilterChain::GetFilter(name);
|
||||
|
||||
if (!filter) throw wxString::Format("Filter not found: %s", name);
|
||||
if (!filter) throw "Filter not found: " + name;
|
||||
|
||||
filters.push_back(filter);
|
||||
}
|
||||
|
||||
wxArrayString AssExporter::GetAllFilterNames() const {
|
||||
wxArrayString names;
|
||||
std::vector<std::string> AssExporter::GetAllFilterNames() const {
|
||||
std::vector<std::string> names;
|
||||
transform(filter_list_begin(), filter_list_end(),
|
||||
std::back_inserter(names), std::mem_fun(&AssExportFilter::GetName));
|
||||
back_inserter(names), std::mem_fun(&AssExportFilter::GetName));
|
||||
return names;
|
||||
}
|
||||
|
||||
@ -102,19 +104,19 @@ AssFile *AssExporter::ExportTransform(wxWindow *export_dialog, bool copy) {
|
||||
return subs;
|
||||
}
|
||||
|
||||
void AssExporter::Export(wxString const& filename, wxString const& charset, wxWindow *export_dialog) {
|
||||
agi::scoped_ptr<AssFile> subs(ExportTransform(export_dialog, true));
|
||||
void AssExporter::Export(agi::fs::path const& filename, std::string const& charset, wxWindow *export_dialog) {
|
||||
std::unique_ptr<AssFile> subs(ExportTransform(export_dialog, true));
|
||||
subs->Save(filename, false, false, charset);
|
||||
}
|
||||
|
||||
wxSizer *AssExporter::GetSettingsSizer(wxString const& name) {
|
||||
wxSizer *AssExporter::GetSettingsSizer(std::string const& name) {
|
||||
auto pos = Sizers.find(name);
|
||||
return pos == Sizers.end() ? nullptr : pos->second;
|
||||
}
|
||||
|
||||
wxString const& AssExporter::GetDescription(wxString const& name) const {
|
||||
std::string const& AssExporter::GetDescription(std::string const& name) const {
|
||||
AssExportFilter *filter = AssExportFilterChain::GetFilter(name);
|
||||
if (filter)
|
||||
return filter->GetDescription();
|
||||
throw wxString::Format("Filter not found: %s", name);
|
||||
throw "Filter not found: " + name;
|
||||
}
|
||||
|
@ -32,16 +32,17 @@
|
||||
/// @ingroup export
|
||||
///
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/string.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class AssExportFilter;
|
||||
class AssFile;
|
||||
namespace agi { struct Context; }
|
||||
class wxSizer;
|
||||
class wxWindow;
|
||||
|
||||
typedef std::vector<AssExportFilter*> FilterList;
|
||||
|
||||
@ -49,7 +50,7 @@ class AssExporter {
|
||||
typedef FilterList::const_iterator filter_iterator;
|
||||
|
||||
/// Sizers for configuration panels
|
||||
std::map<wxString, wxSizer*> Sizers;
|
||||
std::map<std::string, wxSizer*> Sizers;
|
||||
|
||||
/// Filters which will be applied to the subtitles
|
||||
FilterList filters;
|
||||
@ -65,11 +66,11 @@ public:
|
||||
AssExporter(agi::Context *c);
|
||||
|
||||
/// Get the names of all registered export filters
|
||||
wxArrayString GetAllFilterNames() const;
|
||||
std::vector<std::string> GetAllFilterNames() const;
|
||||
|
||||
/// Add the named filter to the list of filters to be run
|
||||
/// @throws wxString if filter is not found
|
||||
void AddFilter(wxString const& name);
|
||||
/// @throws std::string if filter is not found
|
||||
void AddFilter(std::string const& name);
|
||||
|
||||
/// Run all added export filters
|
||||
/// @param parent_window Parent window the filters should use when opening dialogs
|
||||
@ -81,7 +82,7 @@ public:
|
||||
/// @param file Target filename
|
||||
/// @param charset Target charset
|
||||
/// @param parent_window Parent window the filters should use when opening dialogs
|
||||
void Export(wxString const& file, wxString const& charset, wxWindow *parent_window= 0);
|
||||
void Export(agi::fs::path const& file, std::string const& charset, wxWindow *parent_window= 0);
|
||||
|
||||
/// Add configuration panels for all registered filters to the target sizer
|
||||
/// @param parent Parent window for controls
|
||||
@ -89,9 +90,9 @@ public:
|
||||
void DrawSettings(wxWindow *parent, wxSizer *target_sizer);
|
||||
|
||||
/// Get the sizer created by DrawSettings for a specific filter
|
||||
wxSizer *GetSettingsSizer(wxString const& name);
|
||||
wxSizer *GetSettingsSizer(std::string const& name);
|
||||
|
||||
/// Get the description of the named export filter
|
||||
/// @throws wxString if filter is not found
|
||||
wxString const& GetDescription(wxString const& name) const;
|
||||
/// @throws std::string if filter is not found
|
||||
std::string const& GetDescription(std::string const& name) const;
|
||||
};
|
||||
|
@ -35,21 +35,10 @@
|
||||
|
||||
#include "ass_file.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <fstream>
|
||||
#include <inttypes.h>
|
||||
#include <list>
|
||||
|
||||
#include <wx/filename.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include "ass_attachment.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_info.h"
|
||||
#include "ass_style.h"
|
||||
#include "compat.h"
|
||||
#include "options.h"
|
||||
#include "standard_paths.h"
|
||||
#include "subtitle_format.h"
|
||||
@ -57,7 +46,17 @@
|
||||
#include "text_file_writer.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/dispatch.h>
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/of_type_adaptor.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
#include <list>
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
@ -73,16 +72,18 @@ AssFile::AssFile ()
|
||||
}
|
||||
|
||||
AssFile::~AssFile() {
|
||||
background_delete_clear(Line);
|
||||
auto copy = new EntryList;
|
||||
copy->swap(Line);
|
||||
agi::dispatch::Background().Async([=]{ delete copy; });
|
||||
}
|
||||
|
||||
/// @brief Load generic subs
|
||||
void AssFile::Load(const wxString &_filename, wxString const& charset) {
|
||||
const SubtitleFormat *reader = SubtitleFormat::GetReader(_filename);
|
||||
void AssFile::Load(agi::fs::path const& filename, std::string const& charset) {
|
||||
const SubtitleFormat *reader = SubtitleFormat::GetReader(filename);
|
||||
|
||||
try {
|
||||
AssFile temp;
|
||||
reader->ReadFile(&temp, _filename, charset);
|
||||
reader->ReadFile(&temp, filename, charset);
|
||||
|
||||
bool found_style = false;
|
||||
bool found_dialogue = false;
|
||||
@ -109,7 +110,7 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) {
|
||||
|
||||
// Set general data
|
||||
loaded = true;
|
||||
filename = _filename;
|
||||
this->filename = filename;
|
||||
|
||||
// Add comments and set vars
|
||||
SetScriptInfo("ScriptType", "v4.00+");
|
||||
@ -124,7 +125,7 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) {
|
||||
FileOpen(filename);
|
||||
}
|
||||
|
||||
void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxString encoding) {
|
||||
void AssFile::Save(agi::fs::path const& filename, bool setfilename, bool addToRecent, std::string const& encoding) {
|
||||
const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
|
||||
if (!writer)
|
||||
throw "Unknown file type.";
|
||||
@ -132,47 +133,38 @@ void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxStri
|
||||
if (setfilename) {
|
||||
autosavedCommitId = savedCommitId = commitId;
|
||||
this->filename = filename;
|
||||
StandardPaths::SetPathValue("?script", wxFileName(filename).GetPath());
|
||||
StandardPaths::SetPathValue("?script", filename.parent_path());
|
||||
}
|
||||
|
||||
FileSave();
|
||||
|
||||
writer->WriteFile(this, filename, encoding);
|
||||
|
||||
if (addToRecent) {
|
||||
if (addToRecent)
|
||||
AddToRecent(filename);
|
||||
}
|
||||
}
|
||||
|
||||
wxString AssFile::AutoSave() {
|
||||
agi::fs::path AssFile::AutoSave() {
|
||||
if (!loaded || commitId == autosavedCommitId)
|
||||
return "";
|
||||
|
||||
wxFileName origfile(filename);
|
||||
wxString path = to_wx(OPT_GET("Path/Auto/Save")->GetString());
|
||||
if (!path)
|
||||
path = origfile.GetPath();
|
||||
path = StandardPaths::DecodePath(path + "/");
|
||||
auto path = StandardPaths::DecodePath(OPT_GET("Path/Auto/Save")->GetString());
|
||||
if (path.empty())
|
||||
path = filename.parent_path();
|
||||
|
||||
wxFileName dstpath(path);
|
||||
if (!dstpath.DirExists())
|
||||
wxMkdir(path);
|
||||
agi::fs::CreateDirectory(path);
|
||||
|
||||
wxString name = origfile.GetName();
|
||||
if (!name)
|
||||
auto name = filename.filename();
|
||||
if (name.empty())
|
||||
name = "Untitled";
|
||||
dstpath.SetFullName(wxString::Format("%s.%s.AUTOSAVE.ass", name, wxDateTime::Now().Format("%Y-%m-%d-%H-%M-%S")));
|
||||
|
||||
Save(dstpath.GetFullPath(), false, false);
|
||||
path /= str(boost::format("%s.%s.AUTOSAVE.ass") % name % agi::util::strftime("%Y-%m-%d-%H-%M-%S"));
|
||||
|
||||
Save(path, false, false);
|
||||
|
||||
autosavedCommitId = commitId;
|
||||
|
||||
return dstpath.GetFullPath();
|
||||
}
|
||||
|
||||
static void write_line(wxString const& line, std::vector<char>& dst) {
|
||||
wxCharBuffer buffer = (line + "\r\n").utf8_str();
|
||||
copy(buffer.data(), buffer.data() + buffer.length(), back_inserter(dst));
|
||||
return path;
|
||||
}
|
||||
|
||||
void AssFile::SaveMemory(std::vector<char> &dst) {
|
||||
@ -190,9 +182,9 @@ void AssFile::SaveMemory(std::vector<char> &dst) {
|
||||
for (auto const& line : Line) {
|
||||
if (group != line.Group()) {
|
||||
group = line.Group();
|
||||
write_line(line.GroupHeader(), dst);
|
||||
boost::push_back(dst, line.GroupHeader() + "\r\n");
|
||||
}
|
||||
write_line(line.GetEntryData(), dst);
|
||||
boost::push_back(dst, line.GetEntryData() + "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,28 +197,15 @@ bool AssFile::CanSave() const {
|
||||
}
|
||||
}
|
||||
|
||||
void AssFile::Clear() {
|
||||
background_delete_clear(Line);
|
||||
|
||||
loaded = false;
|
||||
filename.clear();
|
||||
UndoStack.clear();
|
||||
RedoStack.clear();
|
||||
undoDescription.clear();
|
||||
}
|
||||
|
||||
void AssFile::LoadDefault(bool defline) {
|
||||
Clear();
|
||||
|
||||
// Write headers
|
||||
Line.push_back(*new AssInfo("Title", "Default Aegisub file"));
|
||||
Line.push_back(*new AssInfo("ScriptType", "v4.00+"));
|
||||
Line.push_back(*new AssInfo("WrapStyle", "0"));
|
||||
Line.push_back(*new AssInfo("ScaledBorderAndShadow", "yes"));
|
||||
Line.push_back(*new AssInfo("Collisions", "Normal"));
|
||||
if (!OPT_GET("Subtitle/Default Resolution/Auto")->GetBool()) {
|
||||
Line.push_back(*new AssInfo("PlayResX", wxString::Format("%" PRId64, OPT_GET("Subtitle/Default Resolution/Width")->GetInt())));
|
||||
Line.push_back(*new AssInfo("PlayResY", wxString::Format("%" PRId64, OPT_GET("Subtitle/Default Resolution/Height")->GetInt())));
|
||||
Line.push_back(*new AssInfo("PlayResX", std::to_string(OPT_GET("Subtitle/Default Resolution/Width")->GetInt())));
|
||||
Line.push_back(*new AssInfo("PlayResY", std::to_string(OPT_GET("Subtitle/Default Resolution/Height")->GetInt())));
|
||||
}
|
||||
Line.push_back(*new AssInfo("YCbCr Matrix", "None"));
|
||||
|
||||
@ -283,40 +262,32 @@ void AssFile::InsertLine(AssEntry *entry) {
|
||||
Line.push_front(*entry);
|
||||
}
|
||||
|
||||
void AssFile::InsertAttachment(wxString filename) {
|
||||
void AssFile::InsertAttachment(agi::fs::path const& filename) {
|
||||
AssEntryGroup group = ENTRY_GRAPHIC;
|
||||
|
||||
wxString ext = filename.Right(4).Lower();
|
||||
auto ext = boost::to_lower_copy(filename.extension().string());
|
||||
if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb")
|
||||
group = ENTRY_FONT;
|
||||
|
||||
std::unique_ptr<AssAttachment> newAttach(new AssAttachment(wxFileName(filename).GetFullName(), group));
|
||||
newAttach->Import(filename);
|
||||
|
||||
InsertLine(newAttach.release());
|
||||
InsertLine(new AssAttachment(filename, group));
|
||||
}
|
||||
|
||||
wxString AssFile::GetScriptInfo(wxString key) const {
|
||||
key.MakeLower();
|
||||
|
||||
std::string AssFile::GetScriptInfo(std::string const& key) const {
|
||||
for (const auto info : Line | agi::of_type<AssInfo>()) {
|
||||
if (key == info->Key().Lower())
|
||||
if (boost::iequals(key, info->Key()))
|
||||
return info->Value();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int AssFile::GetScriptInfoAsInt(wxString const& key) const {
|
||||
long temp = 0;
|
||||
GetScriptInfo(key).ToLong(&temp);
|
||||
return temp;
|
||||
int AssFile::GetScriptInfoAsInt(std::string const& key) const {
|
||||
return atoi(GetScriptInfo(key).c_str());
|
||||
}
|
||||
|
||||
void AssFile::SetScriptInfo(wxString const& key, wxString const& value) {
|
||||
wxString lower_key = key.Lower();
|
||||
void AssFile::SetScriptInfo(std::string const& key, std::string const& value) {
|
||||
for (auto info : Line | agi::of_type<AssInfo>()) {
|
||||
if (lower_key == info->Key().Lower()) {
|
||||
if (boost::iequals(key, info->Key())) {
|
||||
if (value.empty())
|
||||
delete info;
|
||||
else
|
||||
@ -366,10 +337,9 @@ AssStyle *AssFile::GetStyle(std::string const& name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AssFile::AddToRecent(wxString const& file) const {
|
||||
config::mru->Add("Subtitle", from_wx(file));
|
||||
wxFileName filepath(file);
|
||||
OPT_SET("Path/Last/Subtitles")->SetString(from_wx(filepath.GetPath()));
|
||||
void AssFile::AddToRecent(agi::fs::path const& file) const {
|
||||
config::mru->Add("Subtitle", file);
|
||||
OPT_SET("Path/Last/Subtitles")->SetString(file.parent_path().string());
|
||||
}
|
||||
|
||||
int AssFile::Commit(wxString const& desc, int type, int amendId, AssEntry *single_line) {
|
||||
|
@ -33,10 +33,13 @@
|
||||
///
|
||||
|
||||
#include <boost/container/list.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/signal.h>
|
||||
|
||||
#include "ass_entry.h"
|
||||
@ -63,7 +66,7 @@ class AssFile {
|
||||
/// A set of changes has been committed to the file (AssFile::CommitType)
|
||||
agi::signal::Signal<int, std::set<const AssEntry*> const&> AnnounceCommit;
|
||||
/// A new file has been opened (filename)
|
||||
agi::signal::Signal<wxString> FileOpen;
|
||||
agi::signal::Signal<agi::fs::path> FileOpen;
|
||||
/// The file is about to be saved
|
||||
/// This signal is intended for adding metadata such as video filename,
|
||||
/// frame number, etc. Ideally this would all be done immediately rather
|
||||
@ -74,7 +77,7 @@ public:
|
||||
/// The lines in the file
|
||||
EntryList Line;
|
||||
/// The filename of this file, if any
|
||||
wxString filename;
|
||||
agi::fs::path filename;
|
||||
/// Is the file loaded?
|
||||
bool loaded;
|
||||
|
||||
@ -84,9 +87,7 @@ public:
|
||||
~AssFile();
|
||||
|
||||
/// Does the file have unsaved changes?
|
||||
bool IsModified() const {return commitId != savedCommitId; };
|
||||
/// Clear the file
|
||||
void Clear();
|
||||
bool IsModified() const { return commitId != savedCommitId; };
|
||||
|
||||
/// @brief Load default file
|
||||
/// @param defline Add a blank line to the file
|
||||
@ -94,7 +95,7 @@ public:
|
||||
/// Add a line to the file at the end of the appropriate section
|
||||
void InsertLine(AssEntry *line);
|
||||
/// Attach a file to the ass file
|
||||
void InsertAttachment(wxString filename);
|
||||
void InsertAttachment(agi::fs::path const& filename);
|
||||
/// Get the names of all of the styles available
|
||||
std::vector<std::string> GetStyles() const;
|
||||
/// @brief Get a style by name
|
||||
@ -107,24 +108,24 @@ public:
|
||||
/// @brief Load from a file
|
||||
/// @param file File name
|
||||
/// @param charset Character set of file or empty to autodetect
|
||||
void Load(const wxString &file, wxString const& charset="");
|
||||
void Load(agi::fs::path const& file, std::string const& charset="");
|
||||
|
||||
/// @brief Save to a file
|
||||
/// @param file Path to save to
|
||||
/// @param setfilename Should the filename be changed to the passed path?
|
||||
/// @param addToRecent Should the file be added to the MRU list?
|
||||
/// @param encoding Encoding to use, or empty to let the writer decide (which usually means "App/Save Charset")
|
||||
void Save(wxString file,bool setfilename=false,bool addToRecent=true,const wxString encoding="");
|
||||
void Save(agi::fs::path const& file, bool setfilename=false, bool addToRecent=true, std::string const& encoding="");
|
||||
|
||||
/// @brief Autosave the file if there have been any chances since the last autosave
|
||||
/// @return File name used or empty if no save was performed
|
||||
wxString AutoSave();
|
||||
agi::fs::path AutoSave();
|
||||
|
||||
/// @brief Save to a memory buffer. Used for subtitle providers which support it
|
||||
/// @param[out] dst Destination vector
|
||||
void SaveMemory(std::vector<char> &dst);
|
||||
/// Add file name to the MRU list
|
||||
void AddToRecent(wxString const& file) const;
|
||||
void AddToRecent(agi::fs::path const& file) const;
|
||||
/// Can the file be saved in its current format?
|
||||
bool CanSave() const;
|
||||
|
||||
@ -133,11 +134,11 @@ public:
|
||||
/// @param[in] h Height
|
||||
void GetResolution(int &w,int &h) const;
|
||||
/// Get the value in a [Script Info] key as int, or 0 if it is not present
|
||||
int GetScriptInfoAsInt(wxString const& key) const;
|
||||
int GetScriptInfoAsInt(std::string const& key) const;
|
||||
/// Get the value in a [Script Info] key as string.
|
||||
wxString GetScriptInfo(wxString key) const;
|
||||
std::string GetScriptInfo(std::string const& key) const;
|
||||
/// Set the value of a [Script Info] key. Adds it if it doesn't exist.
|
||||
void SetScriptInfo(wxString const& key, wxString const& value);
|
||||
void SetScriptInfo(std::string const& key, std::string const& value);
|
||||
|
||||
/// Type of changes made in a commit
|
||||
enum CommitType {
|
||||
|
@ -16,21 +16,22 @@
|
||||
|
||||
#include "ass_entry.h"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
class AssInfo : public AssEntry {
|
||||
wxString key;
|
||||
wxString value;
|
||||
std::string key;
|
||||
std::string value;
|
||||
|
||||
public:
|
||||
AssInfo(AssInfo const& o) : key(o.key), value(o.value) { }
|
||||
AssInfo(wxString const& line) : key(line.BeforeFirst(':').Trim()), value(line.AfterFirst(':').Trim(false)) { }
|
||||
AssInfo(wxString const& key, wxString const& value) : key(key), value(value) { }
|
||||
AssInfo(std::string const& key, std::string const& value) : key(key), value(value) { }
|
||||
|
||||
AssEntry *Clone() const override { return new AssInfo(*this); }
|
||||
AssEntryGroup Group() const override { return ENTRY_INFO; }
|
||||
const wxString GetEntryData() const override { return key + ": " + value; }
|
||||
wxString GetSSAText() const override { return key.Lower() == "scripttype: v4.00+" ? "ScriptType: v4.00" : GetEntryData(); }
|
||||
const std::string GetEntryData() const override { return key + ": " + value; }
|
||||
std::string GetSSAText() const override { return boost::iequals(key, "scripttype: v4.00+") ? "ScriptType: v4.00" : GetEntryData(); }
|
||||
|
||||
wxString Key() const { return key; }
|
||||
wxString Value() const { return value; }
|
||||
void SetValue(wxString const& new_value) { value = new_value; }
|
||||
std::string Key() const { return key; }
|
||||
std::string Value() const { return value; }
|
||||
void SetValue(std::string const& new_value) { value = new_value; }
|
||||
};
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "compat.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "selection_controller.h"
|
||||
|
||||
@ -161,12 +160,12 @@ void AssKaraoke::ParseSyllables(AssDialogue *line, Syllable &syl) {
|
||||
syls.push_back(syl);
|
||||
}
|
||||
|
||||
wxString AssKaraoke::GetText() const {
|
||||
wxString text;
|
||||
std::string AssKaraoke::GetText() const {
|
||||
std::string text;
|
||||
text.reserve(size() * 10);
|
||||
|
||||
for (auto const& syl : syls)
|
||||
text += to_wx(syl.GetText(true));
|
||||
text += syl.GetText(true);
|
||||
|
||||
return text;
|
||||
}
|
||||
@ -301,7 +300,7 @@ void AssKaraoke::SplitLines(std::set<AssDialogue*> const& lines, agi::Context *c
|
||||
|
||||
new_line->Start = syl.start_time;
|
||||
new_line->End = syl.start_time + syl.duration;
|
||||
new_line->Text = to_wx(syl.GetText(false));
|
||||
new_line->Text = syl.GetText(false);
|
||||
|
||||
c->ass->Line.insert(it, *new_line);
|
||||
|
||||
|
@ -25,8 +25,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <libaegisub/signal.h>
|
||||
|
||||
namespace agi { struct Context; }
|
||||
@ -84,7 +82,7 @@ public:
|
||||
size_t size() const { return syls.size(); }
|
||||
|
||||
/// Get the line's text with k tags
|
||||
wxString GetText() const;
|
||||
std::string GetText() const;
|
||||
|
||||
/// Get the karaoke tag type used, with leading slash
|
||||
/// @returns "\k", "\kf", or "\ko"
|
||||
|
@ -24,8 +24,9 @@
|
||||
#include "subtitle_format.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <wx/log.h>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
|
||||
AssParser::AssParser(AssFile *target, int version)
|
||||
: target(target)
|
||||
@ -39,8 +40,8 @@ AssParser::AssParser(AssFile *target, int version)
|
||||
AssParser::~AssParser() {
|
||||
}
|
||||
|
||||
void AssParser::ParseAttachmentLine(wxString const& data) {
|
||||
bool is_filename = data.StartsWith("fontname: ") || data.StartsWith("filename: ");
|
||||
void AssParser::ParseAttachmentLine(std::string const& data) {
|
||||
bool is_filename = boost::starts_with(data, "fontname: ") || boost::starts_with(data, "filename: ");
|
||||
|
||||
bool valid_data = data.size() > 0 && data.size() <= 80;
|
||||
for (auto byte : data) {
|
||||
@ -60,59 +61,59 @@ void AssParser::ParseAttachmentLine(wxString const& data) {
|
||||
attach->AddData(data);
|
||||
|
||||
// Done building
|
||||
if (data.Length() < 80) {
|
||||
if (data.size() < 80) {
|
||||
attach->Finish();
|
||||
InsertLine(attach.release());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssParser::ParseScriptInfoLine(wxString const& data) {
|
||||
if (data.StartsWith(";")) {
|
||||
void AssParser::ParseScriptInfoLine(std::string const& data) {
|
||||
if (boost::starts_with(data, ";")) {
|
||||
// Skip stupid comments added by other programs
|
||||
// Of course, we'll add our own in place later... ;)
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.StartsWith("ScriptType:")) {
|
||||
wxString versionString = data.Mid(11).Trim(true).Trim(false).Lower();
|
||||
int trueVersion;
|
||||
if (versionString == "v4.00")
|
||||
trueVersion = 0;
|
||||
else if (versionString == "v4.00+")
|
||||
trueVersion = 1;
|
||||
if (boost::starts_with(data, "ScriptType:")) {
|
||||
std::string version_str = data.substr(11);
|
||||
boost::trim(version_str);
|
||||
boost::to_lower(version_str);
|
||||
if (version_str == "v4.00")
|
||||
version = 0;
|
||||
else if (version_str == "v4.00+")
|
||||
version = 1;
|
||||
else
|
||||
throw SubtitleFormatParseError("Unknown SSA file format version", 0);
|
||||
if (trueVersion != version) {
|
||||
wxLogMessage("Warning: File has the wrong extension.");
|
||||
version = trueVersion;
|
||||
}
|
||||
}
|
||||
|
||||
InsertLine(new AssInfo(data));
|
||||
size_t pos = data.find(':');
|
||||
if (pos == data.npos) return;
|
||||
|
||||
InsertLine(new AssInfo(data.substr(0, pos), boost::trim_left_copy(data.substr(pos + 1))));
|
||||
}
|
||||
|
||||
void AssParser::ParseEventLine(wxString const& data) {
|
||||
if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:"))
|
||||
void AssParser::ParseEventLine(std::string const& data) {
|
||||
if (boost::starts_with(data, "Dialogue:") || boost::starts_with(data, "Comment:"))
|
||||
InsertLine(new AssDialogue(data));
|
||||
}
|
||||
|
||||
void AssParser::ParseStyleLine(wxString const& data) {
|
||||
if (data.StartsWith("Style:"))
|
||||
void AssParser::ParseStyleLine(std::string const& data) {
|
||||
if (boost::starts_with(data, "Style:"))
|
||||
InsertLine(new AssStyle(data, version));
|
||||
}
|
||||
|
||||
void AssParser::ParseFontLine(wxString const& data) {
|
||||
if (data.StartsWith("fontname: "))
|
||||
attach.reset(new AssAttachment(data.Mid(10), ENTRY_FONT));
|
||||
void AssParser::ParseFontLine(std::string const& data) {
|
||||
if (boost::starts_with(data, "fontname: "))
|
||||
attach.reset(new AssAttachment(data.substr(10), ENTRY_FONT));
|
||||
}
|
||||
|
||||
void AssParser::ParseGraphicsLine(wxString const& data) {
|
||||
if (data.StartsWith("filename: "))
|
||||
attach.reset(new AssAttachment(data.Mid(10), ENTRY_GRAPHIC));
|
||||
void AssParser::ParseGraphicsLine(std::string const& data) {
|
||||
if (boost::starts_with(data, "filename: "))
|
||||
attach.reset(new AssAttachment(data.substr(10), ENTRY_GRAPHIC));
|
||||
}
|
||||
|
||||
void AssParser::AddLine(wxString const& data) {
|
||||
void AssParser::AddLine(std::string const& data) {
|
||||
// Special-case for attachments since a line could theoretically be both a
|
||||
// valid attachment data line and a valid section header, and if an
|
||||
// attachment is in progress it needs to be treated as that
|
||||
@ -124,10 +125,10 @@ void AssParser::AddLine(wxString const& data) {
|
||||
if (data.empty()) return;
|
||||
|
||||
// Section header
|
||||
if (data[0] == '[' && data.Last() == ']') {
|
||||
if (data[0] == '[' && data.back() == ']') {
|
||||
// Ugly hacks to allow intermixed v4 and v4+ style sections
|
||||
const wxString low = data.Lower();
|
||||
wxString header = data;
|
||||
const std::string low = boost::to_lower_copy(data);
|
||||
std::string header = data;
|
||||
if (low == "[v4 styles]") {
|
||||
header = "[V4+ Styles]";
|
||||
version = 0;
|
||||
|
@ -16,8 +16,6 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "ass_entry.h"
|
||||
|
||||
class AssAttachment;
|
||||
@ -27,20 +25,21 @@ class AssParser {
|
||||
AssFile *target;
|
||||
int version;
|
||||
std::unique_ptr<AssAttachment> attach;
|
||||
void (AssParser::*state)(wxString const&);
|
||||
void (AssParser::*state)(std::string const&);
|
||||
std::array<AssEntry*, ENTRY_GROUP_MAX> insertion_positions;
|
||||
|
||||
void InsertLine(AssEntry *entry);
|
||||
|
||||
void ParseAttachmentLine(wxString const& data);
|
||||
void ParseEventLine(wxString const& data);
|
||||
void ParseStyleLine(wxString const& data);
|
||||
void ParseScriptInfoLine(wxString const& data);
|
||||
void ParseFontLine(wxString const& data);
|
||||
void ParseGraphicsLine(wxString const& data);
|
||||
void UnknownLine(wxString const&) { }
|
||||
void ParseAttachmentLine(std::string const& data);
|
||||
void ParseEventLine(std::string const& data);
|
||||
void ParseStyleLine(std::string const& data);
|
||||
void ParseScriptInfoLine(std::string const& data);
|
||||
void ParseFontLine(std::string const& data);
|
||||
void ParseGraphicsLine(std::string const& data);
|
||||
void UnknownLine(std::string const&) { }
|
||||
public:
|
||||
AssParser(AssFile *target, int version);
|
||||
~AssParser();
|
||||
void AddLine(wxString const& data);
|
||||
|
||||
void AddLine(std::string const& data);
|
||||
};
|
||||
|
@ -35,13 +35,15 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "ass_style.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "subtitle_format.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/split.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <cctype>
|
||||
|
||||
@ -74,42 +76,28 @@ AssStyle::AssStyle()
|
||||
|
||||
namespace {
|
||||
class parser {
|
||||
typedef boost::iterator_range<std::string::const_iterator> string_range;
|
||||
string_range str;
|
||||
std::vector<string_range> tkns;
|
||||
size_t tkn_idx;
|
||||
boost::split_iterator<agi::StringRange::const_iterator> pos;
|
||||
|
||||
string_range next_tok() {
|
||||
if (tkn_idx >= tkns.size())
|
||||
std::string next_tok() {
|
||||
if (pos.eof())
|
||||
throw SubtitleFormatParseError("Malformed style: not enough fields", 0);
|
||||
return trim_copy(tkns[tkn_idx++]);
|
||||
return agi::str(trim_copy(*pos++));
|
||||
}
|
||||
|
||||
public:
|
||||
parser(std::string const& str)
|
||||
: tkn_idx(0)
|
||||
{
|
||||
auto pos = find(str.begin(), str.end(), ':');
|
||||
if (pos != str.end()) {
|
||||
this->str = string_range(pos + 1, str.end());
|
||||
split(tkns, this->str, [](char c) { return c == ','; });
|
||||
}
|
||||
parser(std::string const& str) {
|
||||
auto colon = find(str.begin(), str.end(), ':');
|
||||
if (colon != str.end())
|
||||
pos = agi::Split(agi::StringRange(colon + 1, str.end()), ',');
|
||||
}
|
||||
|
||||
void check_done() const {
|
||||
if (tkn_idx != tkns.size())
|
||||
if (!pos.eof())
|
||||
throw SubtitleFormatParseError("Malformed style: too many fields", 0);
|
||||
}
|
||||
|
||||
std::string next_str() {
|
||||
auto tkn = next_tok();
|
||||
return std::string(begin(tkn), end(tkn));
|
||||
}
|
||||
|
||||
agi::Color next_color() {
|
||||
auto tkn = next_tok();
|
||||
return std::string(begin(tkn), end(tkn));
|
||||
}
|
||||
std::string next_str() { return next_tok(); }
|
||||
agi::Color next_color() { return next_tok(); }
|
||||
|
||||
int next_int() {
|
||||
try {
|
||||
@ -130,13 +118,13 @@ public:
|
||||
}
|
||||
|
||||
void skip_token() {
|
||||
++tkn_idx;
|
||||
if (!pos.eof())
|
||||
++pos;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
AssStyle::AssStyle(wxString const& rawData, int version) {
|
||||
std::string str(from_wx(rawData));
|
||||
AssStyle::AssStyle(std::string const& str, int version) {
|
||||
parser p(str);
|
||||
|
||||
name = p.next_str();
|
||||
@ -211,28 +199,28 @@ void AssStyle::UpdateData() {
|
||||
replace(name.begin(), name.end(), ',', ';');
|
||||
replace(font.begin(), font.end(), ',', ';');
|
||||
|
||||
data = wxString::Format("Style: %s,%s,%g,%s,%s,%s,%s,%d,%d,%d,%d,%g,%g,%g,%g,%d,%g,%g,%i,%i,%i,%i,%i",
|
||||
to_wx(name), to_wx(font), fontsize,
|
||||
primary.GetAssStyleFormatted(),
|
||||
secondary.GetAssStyleFormatted(),
|
||||
outline.GetAssStyleFormatted(),
|
||||
shadow.GetAssStyleFormatted(),
|
||||
(bold? -1 : 0), (italic ? -1 : 0),
|
||||
(underline?-1:0),(strikeout?-1:0),
|
||||
scalex,scaley,spacing,angle,
|
||||
borderstyle,outline_w,shadow_w,alignment,
|
||||
Margin[0],Margin[1],Margin[2],encoding);
|
||||
data = str(boost::format("Style: %s,%s,%g,%s,%s,%s,%s,%d,%d,%d,%d,%g,%g,%g,%g,%d,%g,%g,%i,%i,%i,%i,%i")
|
||||
% name % font % fontsize
|
||||
% primary.GetAssStyleFormatted()
|
||||
% secondary.GetAssStyleFormatted()
|
||||
% outline.GetAssStyleFormatted()
|
||||
% shadow.GetAssStyleFormatted()
|
||||
% (bold? -1 : 0) % (italic ? -1 : 0)
|
||||
% (underline ? -1 : 0) % (strikeout ? -1 : 0)
|
||||
% scalex % scaley % spacing % angle
|
||||
% borderstyle % outline_w % shadow_w % alignment
|
||||
% Margin[0] % Margin[1] % Margin[2] % encoding);
|
||||
}
|
||||
|
||||
wxString AssStyle::GetSSAText() const {
|
||||
return wxString::Format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i",
|
||||
to_wx(name), to_wx(font), fontsize,
|
||||
primary.GetSsaFormatted(),
|
||||
secondary.GetSsaFormatted(),
|
||||
shadow.GetSsaFormatted(),
|
||||
(bold? -1 : 0), (italic ? -1 : 0),
|
||||
borderstyle,outline_w,shadow_w,AssToSsa(alignment),
|
||||
Margin[0],Margin[1],Margin[2],encoding);
|
||||
std::string AssStyle::GetSSAText() const {
|
||||
return str(boost::format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i")
|
||||
% name % font % fontsize
|
||||
% primary.GetSsaFormatted()
|
||||
% secondary.GetSsaFormatted()
|
||||
% shadow.GetSsaFormatted()
|
||||
% (bold? -1 : 0) % (italic ? -1 : 0)
|
||||
% borderstyle % outline_w % shadow_w % AssToSsa(alignment)
|
||||
% Margin[0] % Margin[1] % Margin[2] % encoding);
|
||||
}
|
||||
|
||||
AssEntry *AssStyle::Clone() const {
|
||||
|
@ -40,7 +40,7 @@
|
||||
#include <wx/arrstr.h>
|
||||
|
||||
class AssStyle : public AssEntry {
|
||||
wxString data;
|
||||
std::string data;
|
||||
|
||||
public:
|
||||
std::string name; ///< Name of the style; must be case-insensitively unique within a file despite being case-sensitive
|
||||
@ -75,10 +75,10 @@ public:
|
||||
static void GetEncodings(wxArrayString &encodingStrings);
|
||||
|
||||
AssStyle();
|
||||
AssStyle(wxString const& data, int version=1);
|
||||
AssStyle(std::string const& data, int version=1);
|
||||
|
||||
const wxString GetEntryData() const override { return data; }
|
||||
wxString GetSSAText() const override;
|
||||
const std::string GetEntryData() const override { return data; }
|
||||
std::string GetSSAText() const override;
|
||||
AssEntryGroup Group() const override { return ENTRY_STYLE; }
|
||||
AssEntry *Clone() const override;
|
||||
|
||||
|
@ -42,8 +42,10 @@
|
||||
#include "text_file_writer.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/fs.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <functional>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
AssStyleStorage::~AssStyleStorage() {
|
||||
delete_clear(style);
|
||||
@ -52,16 +54,14 @@ AssStyleStorage::~AssStyleStorage() {
|
||||
void AssStyleStorage::Save() const {
|
||||
if (storage_name.empty()) return;
|
||||
|
||||
wxString dirname = StandardPaths::DecodePath("?user/catalog/");
|
||||
if (!wxDirExists(dirname) && !wxMkdir(dirname))
|
||||
throw "Failed creating directory for style catalogs";
|
||||
agi::fs::CreateDirectory(StandardPaths::DecodePath("?user/catalog/"));
|
||||
|
||||
TextFileWriter file(StandardPaths::DecodePath("?user/catalog/" + storage_name + ".sty"), "UTF-8");
|
||||
for (const AssStyle *cur : style)
|
||||
file.WriteLineToFile(cur->GetEntryData());
|
||||
}
|
||||
|
||||
void AssStyleStorage::Load(wxString const& name) {
|
||||
void AssStyleStorage::Load(std::string const& name) {
|
||||
storage_name = name;
|
||||
Clear();
|
||||
|
||||
@ -69,17 +69,14 @@ void AssStyleStorage::Load(wxString const& name) {
|
||||
TextFileReader file(StandardPaths::DecodePath("?user/catalog/" + name + ".sty"), "UTF-8");
|
||||
|
||||
while (file.HasMoreLines()) {
|
||||
wxString data = file.ReadLineFromFile();
|
||||
if (data.StartsWith("Style:")) {
|
||||
try {
|
||||
style.push_back(new AssStyle(data));
|
||||
} catch(...) {
|
||||
/* just ignore invalid lines for now */
|
||||
}
|
||||
try {
|
||||
style.push_back(new AssStyle(file.ReadLineFromFile()));
|
||||
} catch(...) {
|
||||
/* just ignore invalid lines for now */
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (agi::FileNotAccessibleError const&) {
|
||||
catch (agi::fs::FileNotAccessible const&) {
|
||||
// Just treat a missing file as an empty file
|
||||
}
|
||||
}
|
||||
|
@ -36,12 +36,10 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
class AssStyle;
|
||||
|
||||
class AssStyleStorage {
|
||||
wxString storage_name;
|
||||
std::string storage_name;
|
||||
std::deque<AssStyle*> style;
|
||||
|
||||
public:
|
||||
@ -77,5 +75,5 @@ public:
|
||||
|
||||
/// Load stored styles from a file
|
||||
/// @param name Catalog name (note: not file name)
|
||||
void Load(wxString const& name);
|
||||
void Load(std::string const& name);
|
||||
};
|
||||
|
@ -1,29 +1,16 @@
|
||||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
@ -36,61 +23,64 @@
|
||||
|
||||
#include "ass_time.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
|
||||
AssTime::AssTime(int time) : time(mid(0, time, 10 * 60 * 60 * 1000 - 1)) { }
|
||||
|
||||
AssTime::AssTime(wxString const& text)
|
||||
AssTime::AssTime(std::string const& text)
|
||||
: time(0)
|
||||
{
|
||||
size_t pos = 0, end = 0;
|
||||
|
||||
int colons = text.Freq(':');
|
||||
|
||||
// Set start so that there are only two colons at most
|
||||
for (; colons > 2; --colons) pos = text.find(':', pos) + 1;
|
||||
|
||||
// Hours
|
||||
if (colons == 2) {
|
||||
while (text[end++] != ':') { }
|
||||
time += AegiStringToInt(text, pos, end) * 60 * 60 * 1000;
|
||||
pos = end;
|
||||
int after_decimal = -1;
|
||||
int current = 0;
|
||||
for (char c : text | boost::adaptors::filtered(boost::is_any_of(":0123456789."))) {
|
||||
if (c == ':') {
|
||||
time = time * 60 + current;
|
||||
current = 0;
|
||||
}
|
||||
else if (c == '.') {
|
||||
time = (time * 60 + current) * 1000;
|
||||
current = 0;
|
||||
after_decimal = 100;
|
||||
}
|
||||
else if (after_decimal < 0) {
|
||||
current *= 10;
|
||||
current += c - '0';
|
||||
}
|
||||
else {
|
||||
time += (c - '0') * after_decimal;
|
||||
after_decimal /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
// Minutes
|
||||
if (colons >= 1) {
|
||||
while (text[end++] != ':') { }
|
||||
time += AegiStringToInt(text, pos, end) * 60 * 1000;
|
||||
}
|
||||
|
||||
// Milliseconds (includes seconds)
|
||||
time += AegiStringToFix(text, 3, end, text.size());
|
||||
// Never saw a decimal, so convert now to ms
|
||||
if (after_decimal < 0)
|
||||
time = (time * 60 + current) * 1000;
|
||||
|
||||
// Limit to the valid range
|
||||
time = mid(0, time, 10 * 60 * 60 * 1000 - 1);
|
||||
}
|
||||
|
||||
wxString AssTime::GetAssFormated(bool msPrecision) const {
|
||||
wxChar ret[] = {
|
||||
'0' + GetTimeHours(),
|
||||
':',
|
||||
'0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10),
|
||||
'0' + (time % (10 * 60 * 1000)) / (60 * 1000),
|
||||
':',
|
||||
'0' + (time % (60 * 1000)) / (1000 * 10),
|
||||
'0' + (time % (10 * 1000)) / 1000,
|
||||
'.',
|
||||
'0' + (time % 1000) / 100,
|
||||
'0' + (time % 100) / 10,
|
||||
'0' + time % 10
|
||||
};
|
||||
|
||||
return wxString(ret, msPrecision ? 11 : 10);
|
||||
std::string AssTime::GetAssFormated(bool msPrecision) const {
|
||||
std::string ret(10 + msPrecision, ':');
|
||||
ret[0] = '0' + GetTimeHours();
|
||||
ret[2] = '0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10);
|
||||
ret[3] = '0' + (time % (10 * 60 * 1000)) / (60 * 1000);
|
||||
ret[5] = '0' + (time % (60 * 1000)) / (1000 * 10);
|
||||
ret[6] = '0' + (time % (10 * 1000)) / 1000;
|
||||
ret[7] = '.';
|
||||
ret[8] = '0' + (time % 1000) / 100;
|
||||
ret[9] = '0' + (time % 100) / 10;
|
||||
if (msPrecision)
|
||||
ret[10] = '0' + time % 10;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int AssTime::GetTimeHours() const { return time / 3600000; }
|
||||
@ -99,25 +89,27 @@ int AssTime::GetTimeSeconds() const { return (time % 60000) / 1000; }
|
||||
int AssTime::GetTimeMiliseconds() const { return (time % 1000); }
|
||||
int AssTime::GetTimeCentiseconds() const { return (time % 1000) / 10; }
|
||||
|
||||
SmpteFormatter::SmpteFormatter(agi::vfr::Framerate fps, char sep)
|
||||
SmpteFormatter::SmpteFormatter(agi::vfr::Framerate fps, std::string const& sep)
|
||||
: fps(fps)
|
||||
, sep(sep)
|
||||
{
|
||||
}
|
||||
|
||||
wxString SmpteFormatter::ToSMPTE(AssTime time) const {
|
||||
std::string SmpteFormatter::ToSMPTE(AssTime time) const {
|
||||
int h=0, m=0, s=0, f=0;
|
||||
fps.SmpteAtTime(time, &h, &m, &s, &f);
|
||||
return wxString::Format("%02d%c%02d%c%02d%c%02d", h, sep, m, sep, s, sep, f);
|
||||
return str(boost::format("%02d%s%02d%s%02d%c%02d") % h % sep % m % sep % s % sep % f);
|
||||
}
|
||||
|
||||
AssTime SmpteFormatter::FromSMPTE(wxString const& str) const {
|
||||
long h, m, s, f;
|
||||
wxArrayString toks = wxStringTokenize(str, sep);
|
||||
AssTime SmpteFormatter::FromSMPTE(std::string const& str) const {
|
||||
std::vector<std::string> toks;
|
||||
boost::split(toks, str, boost::is_any_of(sep));
|
||||
if (toks.size() != 4) return 0;
|
||||
toks[0].ToLong(&h);
|
||||
toks[1].ToLong(&m);
|
||||
toks[2].ToLong(&s);
|
||||
toks[3].ToLong(&f);
|
||||
|
||||
int h, m, s, f;
|
||||
agi::util::try_parse(toks[0], &h);
|
||||
agi::util::try_parse(toks[1], &m);
|
||||
agi::util::try_parse(toks[2], &s);
|
||||
agi::util::try_parse(toks[3], &f);
|
||||
return fps.TimeAtSmpte(h, m, s, f);
|
||||
}
|
||||
|
@ -1,29 +1,16 @@
|
||||
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
||||
// All rights reserved.
|
||||
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
@ -34,7 +21,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <string>
|
||||
|
||||
#include <libaegisub/vfr.h>
|
||||
|
||||
@ -44,7 +31,7 @@ class AssTime {
|
||||
|
||||
public:
|
||||
AssTime(int ms = 0);
|
||||
AssTime(wxString const& text);
|
||||
AssTime(std::string const& text);
|
||||
|
||||
/// Get millisecond, rounded to centisecond precision
|
||||
operator int() const { return time / 10 * 10; }
|
||||
@ -57,7 +44,7 @@ public:
|
||||
|
||||
/// Return the time as a string
|
||||
/// @param ms Use milliseconds precision, for non-ASS formats
|
||||
wxString GetAssFormated(bool ms=false) const;
|
||||
std::string GetAssFormated(bool ms=false) const;
|
||||
};
|
||||
|
||||
/// @class SmpteFormatter
|
||||
@ -66,13 +53,13 @@ class SmpteFormatter {
|
||||
/// Frame rate to use
|
||||
agi::vfr::Framerate fps;
|
||||
/// Separator character
|
||||
char sep;
|
||||
std::string sep;
|
||||
|
||||
public:
|
||||
SmpteFormatter(agi::vfr::Framerate fps, char sep=':');
|
||||
SmpteFormatter(agi::vfr::Framerate fps, std::string const& sep=":");
|
||||
|
||||
/// Convert an AssTime to a SMPTE timecode
|
||||
wxString ToSMPTE(AssTime time) const;
|
||||
std::string ToSMPTE(AssTime time) const;
|
||||
/// Convert a SMPTE timecode to an AssTime
|
||||
AssTime FromSMPTE(wxString const& str) const;
|
||||
AssTime FromSMPTE(std::string const& str) const;
|
||||
};
|
||||
|
@ -34,14 +34,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <wx/filename.h>
|
||||
|
||||
#include <libaegisub/io.h>
|
||||
#include "audio_controller.h"
|
||||
|
||||
#include "ass_file.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_provider_dummy.h"
|
||||
#include "audio_timing.h"
|
||||
#include "compat.h"
|
||||
@ -51,10 +46,14 @@
|
||||
#include "pen.h"
|
||||
#include "options.h"
|
||||
#include "selection_controller.h"
|
||||
#include "standard_paths.h"
|
||||
#include "utils.h"
|
||||
#include "video_context.h"
|
||||
|
||||
#include <libaegisub/io.h>
|
||||
#include <libaegisub/path.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
AudioController::AudioController(agi::Context *context)
|
||||
: context(context)
|
||||
, subtitle_save_slot(context->ass->AddFileSaveListener(&AudioController::OnSubtitlesSave, this))
|
||||
@ -81,13 +80,11 @@ AudioController::AudioController(agi::Context *context)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
AudioController::~AudioController()
|
||||
{
|
||||
CloseAudio();
|
||||
}
|
||||
|
||||
|
||||
void AudioController::OnPlaybackTimer(wxTimerEvent &)
|
||||
{
|
||||
int64_t pos = player->GetCurrentPosition();
|
||||
@ -105,7 +102,6 @@ void AudioController::OnPlaybackTimer(wxTimerEvent &)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef wxHAS_POWER_EVENTS
|
||||
void AudioController::OnComputerSuspending(wxPowerEvent &)
|
||||
{
|
||||
@ -114,7 +110,6 @@ void AudioController::OnComputerSuspending(wxPowerEvent &)
|
||||
player = 0;
|
||||
}
|
||||
|
||||
|
||||
void AudioController::OnComputerResuming(wxPowerEvent &)
|
||||
{
|
||||
if (provider)
|
||||
@ -154,25 +149,25 @@ void AudioController::OnAudioProviderChanged()
|
||||
{
|
||||
if (IsAudioOpen())
|
||||
// url is cloned because CloseAudio clears it and OpenAudio takes a const reference
|
||||
OpenAudio(audio_url.Clone());
|
||||
OpenAudio(agi::fs::path(audio_url));
|
||||
}
|
||||
|
||||
|
||||
void AudioController::OpenAudio(const wxString &url)
|
||||
void AudioController::OpenAudio(agi::fs::path const& url)
|
||||
{
|
||||
if (!url)
|
||||
if (url.empty())
|
||||
throw agi::InternalError("AudioController::OpenAudio() was passed an empty string. This must not happen.", 0);
|
||||
|
||||
AudioProvider *new_provider = 0;
|
||||
try {
|
||||
new_provider = AudioProviderFactory::GetProvider(url);
|
||||
StandardPaths::SetPathValue("?audio", wxFileName(url).GetPath());
|
||||
config::path->SetToken("?audio", url);
|
||||
}
|
||||
catch (agi::UserCancelException const&) {
|
||||
throw;
|
||||
}
|
||||
catch (...) {
|
||||
config::mru->Remove("Audio", from_wx(url));
|
||||
config::mru->Remove("Audio", url);
|
||||
throw;
|
||||
}
|
||||
|
||||
@ -192,11 +187,10 @@ void AudioController::OpenAudio(const wxString &url)
|
||||
|
||||
audio_url = url;
|
||||
|
||||
config::mru->Add("Audio", from_wx(url));
|
||||
config::mru->Add("Audio", url);
|
||||
|
||||
try
|
||||
{
|
||||
// Tell listeners about this.
|
||||
AnnounceAudioOpen(provider);
|
||||
}
|
||||
catch (...)
|
||||
@ -206,7 +200,6 @@ void AudioController::OpenAudio(const wxString &url)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioController::CloseAudio()
|
||||
{
|
||||
Stop();
|
||||
@ -218,7 +211,7 @@ void AudioController::CloseAudio()
|
||||
|
||||
audio_url.clear();
|
||||
|
||||
StandardPaths::SetPathValue("?audio", "");
|
||||
config::path->SetToken("?audio", "");
|
||||
|
||||
AnnounceAudioClose();
|
||||
}
|
||||
@ -229,12 +222,6 @@ bool AudioController::IsAudioOpen() const
|
||||
return player && provider;
|
||||
}
|
||||
|
||||
|
||||
wxString AudioController::GetAudioURL() const
|
||||
{
|
||||
return audio_url;
|
||||
}
|
||||
|
||||
void AudioController::SetTimingController(AudioTimingController *new_controller)
|
||||
{
|
||||
if (timing_controller.get() != new_controller) {
|
||||
@ -259,13 +246,9 @@ void AudioController::OnTimingControllerUpdatedPrimaryRange()
|
||||
void AudioController::OnSubtitlesSave()
|
||||
{
|
||||
if (IsAudioOpen())
|
||||
{
|
||||
context->ass->SetScriptInfo("Audio URI", MakeRelativePath(audio_url, context->ass->filename));
|
||||
}
|
||||
context->ass->SetScriptInfo("Audio URI", config::path->MakeRelative(audio_url, "?script").generic_string());
|
||||
else
|
||||
{
|
||||
context->ass->SetScriptInfo("Audio URI", "");
|
||||
}
|
||||
}
|
||||
|
||||
void AudioController::PlayRange(const TimeRange &range)
|
||||
@ -402,13 +385,13 @@ int64_t AudioController::MillisecondsFromSamples(int64_t samples) const
|
||||
return millisamples / sr;
|
||||
}
|
||||
|
||||
void AudioController::SaveClip(wxString const& filename, TimeRange const& range) const
|
||||
void AudioController::SaveClip(agi::fs::path const& filename, TimeRange const& range) const
|
||||
{
|
||||
int64_t start_sample = SamplesFromMilliseconds(range.begin());
|
||||
int64_t end_sample = SamplesFromMilliseconds(range.end());
|
||||
if (filename.empty() || start_sample > provider->GetNumSamples() || range.length() == 0) return;
|
||||
|
||||
agi::io::Save outfile(from_wx(filename), true);
|
||||
agi::io::Save outfile(filename, true);
|
||||
std::ofstream& out(outfile.Get());
|
||||
|
||||
size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels();
|
||||
|
@ -33,20 +33,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/pen.h>
|
||||
#include <wx/power.h>
|
||||
|
||||
#include <libaegisub/exception.h>
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
#include <libaegisub/signal.h>
|
||||
|
||||
class AudioPlayer;
|
||||
@ -96,10 +92,10 @@ class AudioController : public wxEvtHandler {
|
||||
AudioProvider *provider;
|
||||
|
||||
/// The current timing mode, if any; owned by the audio controller
|
||||
agi::scoped_ptr<AudioTimingController> timing_controller;
|
||||
std::unique_ptr<AudioTimingController> timing_controller;
|
||||
|
||||
/// The URL of the currently open audio, if any
|
||||
wxString audio_url;
|
||||
agi::fs::path audio_url;
|
||||
|
||||
|
||||
enum PlaybackMode {
|
||||
@ -157,13 +153,9 @@ public:
|
||||
/// @brief Destructor
|
||||
~AudioController();
|
||||
|
||||
|
||||
/// @brief Open an audio stream
|
||||
/// @param url URL of the stream to open
|
||||
///
|
||||
/// The URL can either be a plain filename (with no qualifiers) or one
|
||||
/// recognised by various providers.
|
||||
void OpenAudio(const wxString &url);
|
||||
void OpenAudio(agi::fs::path const& url);
|
||||
|
||||
/// @brief Closes the current audio stream
|
||||
void CloseAudio();
|
||||
@ -177,7 +169,7 @@ public:
|
||||
///
|
||||
/// The returned URL can be passed into OpenAudio() later to open the same
|
||||
/// stream again.
|
||||
wxString GetAudioURL() const;
|
||||
agi::fs::path GetAudioURL() const { return audio_url; }
|
||||
|
||||
|
||||
/// @brief Start or restart audio playback, playing a range
|
||||
@ -263,7 +255,7 @@ public:
|
||||
/// @brief Save a portion of the decoded loaded audio to a wav file
|
||||
/// @param filename File to save to
|
||||
/// @param range Time range to save
|
||||
void SaveClip(wxString const& filename, TimeRange const& range) const;
|
||||
void SaveClip(agi::fs::path const& filename, TimeRange const& range) const;
|
||||
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener)
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener)
|
||||
|
@ -963,7 +963,7 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
|
||||
if (show_time)
|
||||
{
|
||||
AssTime new_label_time = TimeFromAbsoluteX(track_cursor_pos);
|
||||
track_cursor_label = new_label_time.GetAssFormated();
|
||||
track_cursor_label = to_wx(new_label_time.GetAssFormated());
|
||||
track_cursor_label_rect.x += new_pos - old_pos;
|
||||
RefreshRect(track_cursor_label_rect, false);
|
||||
}
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include "audio_player_pulse.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "compat.h"
|
||||
#include "options.h"
|
||||
|
||||
AudioPlayer::AudioPlayer(AudioProvider *provider)
|
||||
|
@ -369,8 +369,7 @@ AlsaPlayer::AlsaPlayer(AudioProvider *provider)
|
||||
{
|
||||
ps->provider = provider;
|
||||
|
||||
wxString device_name = to_wx(OPT_GET("Player/Audio/ALSA/Device")->GetString());
|
||||
ps->device_name = std::string(device_name.utf8_str());
|
||||
ps->device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString();
|
||||
|
||||
if (pthread_create(&thread, 0, &playback_thread, ps.get()) != 0)
|
||||
throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0);
|
||||
|
@ -75,7 +75,7 @@ void OSSPlayer::OpenStream()
|
||||
|
||||
// Open device
|
||||
wxString device = to_wx(OPT_GET("Player/Audio/OSS/Device")->GetString());
|
||||
dspdev = ::open(device.mb_str(wxConvUTF8), O_WRONLY, 0);
|
||||
dspdev = ::open(device.utf8_str(), O_WRONLY, 0);
|
||||
if (dspdev < 0) {
|
||||
throw OSSError("OSS player: opening device failed", 0);
|
||||
}
|
||||
|
@ -32,11 +32,8 @@
|
||||
/// @ingroup audio_input
|
||||
///
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "audio_provider_avs.h"
|
||||
#include "audio_provider_convert.h"
|
||||
#include "audio_provider_dummy.h"
|
||||
@ -47,13 +44,13 @@
|
||||
#include "audio_provider_ram.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "compat.h"
|
||||
#include "dialog_progress.h"
|
||||
#include "frame_main.h"
|
||||
#include "main.h"
|
||||
#include "options.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
|
||||
@ -126,7 +123,7 @@ struct provider_creator {
|
||||
LOG_I("audio_provider") << "Using audio provider: " << name;
|
||||
return provider;
|
||||
}
|
||||
catch (agi::FileNotFoundError const& err) {
|
||||
catch (agi::fs::FileNotFound const& err) {
|
||||
LOG_D("audio_provider") << err.GetChainedMessage();
|
||||
msg += name + ": " + err.GetMessage() + " not found.\n";
|
||||
}
|
||||
@ -147,7 +144,7 @@ struct provider_creator {
|
||||
};
|
||||
}
|
||||
|
||||
AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename) {
|
||||
AudioProvider *AudioProviderFactory::GetProvider(agi::fs::path const& filename) {
|
||||
provider_creator creator;
|
||||
AudioProvider *provider = nullptr;
|
||||
|
||||
@ -172,7 +169,7 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename) {
|
||||
throw agi::AudioProviderOpenError(creator.msg, 0);
|
||||
if (creator.found_file)
|
||||
throw agi::AudioDataNotFoundError(creator.msg, 0);
|
||||
throw agi::FileNotFoundError(from_wx(filename));
|
||||
throw agi::fs::FileNotFound(filename);
|
||||
}
|
||||
|
||||
bool needsCache = provider->NeedsCache();
|
||||
@ -206,4 +203,4 @@ void AudioProviderFactory::RegisterProviders() {
|
||||
#endif
|
||||
}
|
||||
|
||||
template<> AudioProviderFactory::map *FactoryBase<AudioProvider *(*)(wxString)>::classes = nullptr;
|
||||
template<> AudioProviderFactory::map *FactoryBase<AudioProvider *(*)(agi::fs::path)>::classes = nullptr;
|
||||
|
@ -38,38 +38,38 @@
|
||||
#include "audio_provider_avs.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "charset_conv.h"
|
||||
#include "compat.h"
|
||||
#include "options.h"
|
||||
#include "standard_paths.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <wx/filename.h>
|
||||
#include <libaegisub/access.h>
|
||||
#include <libaegisub/charset_conv.h>
|
||||
#include <libaegisub/fs.h>
|
||||
|
||||
AvisynthAudioProvider::AvisynthAudioProvider(wxString filename) {
|
||||
#include <mutex>
|
||||
|
||||
AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) {
|
||||
this->filename = filename;
|
||||
|
||||
wxMutexLocker lock(avs_wrapper.GetMutex());
|
||||
agi::acs::CheckFileRead(filename);
|
||||
|
||||
wxFileName fn(filename);
|
||||
if (!fn.FileExists())
|
||||
throw agi::FileNotFoundError(from_wx(filename));
|
||||
std::lock_guard<std::mutex> lock(avs_wrapper.GetMutex());
|
||||
|
||||
try {
|
||||
IScriptEnvironment *env = avs_wrapper.GetEnv();
|
||||
|
||||
// Include
|
||||
if (filename.EndsWith(".avs"))
|
||||
LoadFromClip(env->Invoke("Import", env->SaveString(fn.GetShortPath().mb_str(csConvLocal))));
|
||||
if (agi::fs::HasExtension(filename, "avs"))
|
||||
LoadFromClip(env->Invoke("Import", env->SaveString(agi::fs::ShortName(filename).c_str())));
|
||||
// Use DirectShowSource
|
||||
else {
|
||||
const char * argnames[3] = { 0, "video", "audio" };
|
||||
AVSValue args[3] = { env->SaveString(fn.GetShortPath().mb_str(csConvLocal)), false, true };
|
||||
AVSValue args[3] = { env->SaveString(agi::fs::ShortName(filename).c_str()), false, true };
|
||||
|
||||
// Load DirectShowSource.dll from app dir if it exists
|
||||
wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll"));
|
||||
if (dsspath.FileExists())
|
||||
env->Invoke("LoadPlugin", env->SaveString(dsspath.GetShortPath().mb_str(csConvLocal)));
|
||||
agi::fs::path dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll"));
|
||||
if (agi::fs::FileExists(dsspath))
|
||||
env->Invoke("LoadPlugin", env->SaveString(agi::fs::ShortName(dsspath).c_str()));
|
||||
|
||||
// Load audio with DSS if it exists
|
||||
if (env->FunctionExists("DirectShowSource"))
|
||||
|
@ -46,7 +46,7 @@ class AvisynthAudioProvider : public AudioProvider {
|
||||
void FillBuffer(void *buf, int64_t start, int64_t count) const;
|
||||
|
||||
public:
|
||||
AvisynthAudioProvider(wxString filename);
|
||||
AvisynthAudioProvider(agi::fs::path const& filename);
|
||||
|
||||
bool AreSamplesNativeEndian() const { return true; }
|
||||
bool NeedsCache() const { return true; }
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
}
|
||||
|
||||
bool AreSamplesNativeEndian() const { return true; }
|
||||
wxString GetFilename() const { return source->GetFilename(); }
|
||||
agi::fs::path GetFilename() const { return source->GetFilename(); }
|
||||
};
|
||||
|
||||
/// Anything integral -> 16 bit signed machine-endian audio converter
|
||||
|
@ -37,7 +37,9 @@
|
||||
#include "audio_provider_dummy.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <wx/uri.h>
|
||||
#include <libaegisub/fs.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
/*
|
||||
* scheme ::= "dummy-audio" ":" signal-specifier "?" signal-parameters
|
||||
@ -59,13 +61,11 @@
|
||||
* in every channel even if one would be LFE.
|
||||
* "ln", length of signal in samples. ln/sr gives signal length in seconds.
|
||||
*/
|
||||
DummyAudioProvider::DummyAudioProvider(wxString uri)
|
||||
{
|
||||
wxURI parsed(uri);
|
||||
if (parsed.GetScheme() != "dummy-audio")
|
||||
throw agi::FileNotFoundError("Not a dummy audio URI");
|
||||
DummyAudioProvider::DummyAudioProvider(agi::fs::path const& uri) {
|
||||
if (!boost::starts_with(uri.string(), "dummy-audio:"))
|
||||
throw agi::fs::FileNotFound(std::string("Not a dummy audio URI"));
|
||||
|
||||
noise = parsed.GetPath() == "noise";
|
||||
noise = boost::contains(uri.string(), ":noise?");
|
||||
channels = 1;
|
||||
sample_rate = 44100;
|
||||
bytes_per_sample = 2;
|
||||
|
@ -39,7 +39,7 @@ class DummyAudioProvider : public AudioProvider {
|
||||
void FillBuffer(void *buf, int64_t start, int64_t count) const;
|
||||
|
||||
public:
|
||||
DummyAudioProvider(wxString uri);
|
||||
DummyAudioProvider(agi::fs::path const& uri);
|
||||
|
||||
bool AreSamplesNativeEndian() const { return true; }
|
||||
};
|
||||
|
@ -35,22 +35,18 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_FFMS2
|
||||
|
||||
#ifdef WIN32
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "audio_provider_ffmpegsource.h"
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "compat.h"
|
||||
#include "options.h"
|
||||
|
||||
#include <libaegisub/fs.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param filename The filename to open
|
||||
FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename) try
|
||||
FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(agi::fs::path const& filename) try
|
||||
: AudioSource(nullptr, FFMS_DestroyAudioSource)
|
||||
{
|
||||
ErrInfo.Buffer = FFMSErrMsg;
|
||||
@ -61,26 +57,24 @@ FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename) try
|
||||
|
||||
LoadAudio(filename);
|
||||
}
|
||||
catch (wxString const& err) {
|
||||
throw agi::AudioProviderOpenError(from_wx(err), 0);
|
||||
catch (std::string const& err) {
|
||||
throw agi::AudioProviderOpenError(err, 0);
|
||||
}
|
||||
catch (const char *err) {
|
||||
throw agi::AudioProviderOpenError(err, 0);
|
||||
}
|
||||
|
||||
void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
|
||||
wxString FileNameShort = wxFileName(filename).GetShortPath();
|
||||
|
||||
FFMS_Indexer *Indexer = FFMS_CreateIndexer(FileNameShort.utf8_str(), &ErrInfo);
|
||||
void FFmpegSourceAudioProvider::LoadAudio(agi::fs::path const& filename) {
|
||||
FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.string().c_str(), &ErrInfo);
|
||||
if (!Indexer) {
|
||||
if (ErrInfo.SubType == FFMS_ERROR_FILE_READ)
|
||||
throw agi::FileNotFoundError(ErrInfo.Buffer);
|
||||
throw agi::fs::FileNotFound(std::string(ErrInfo.Buffer));
|
||||
else
|
||||
throw agi::AudioDataNotFoundError(ErrInfo.Buffer, 0);
|
||||
}
|
||||
|
||||
std::map<int,wxString> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
|
||||
if (TrackList.size() <= 0)
|
||||
std::map<int, std::string> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
|
||||
if (TrackList.empty())
|
||||
throw agi::AudioDataNotFoundError("no audio tracks found", 0);
|
||||
|
||||
// initialize the track number to an invalid value so we can detect later on
|
||||
@ -94,13 +88,13 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
|
||||
}
|
||||
|
||||
// generate a name for the cache file
|
||||
wxString CacheName = GetCacheFilename(filename);
|
||||
agi::fs::path CacheName = GetCacheFilename(filename);
|
||||
|
||||
// try to read index
|
||||
agi::scoped_holder<FFMS_Index*, void (FFMS_CC*)(FFMS_Index*)>
|
||||
Index(FFMS_ReadIndex(CacheName.utf8_str(), &ErrInfo), FFMS_DestroyIndex);
|
||||
Index(FFMS_ReadIndex(CacheName.string().c_str(), &ErrInfo), FFMS_DestroyIndex);
|
||||
|
||||
if (Index && FFMS_IndexBelongsToFile(Index, FileNameShort.utf8_str(), &ErrInfo))
|
||||
if (Index && FFMS_IndexBelongsToFile(Index, filename.string().c_str(), &ErrInfo))
|
||||
Index = nullptr;
|
||||
|
||||
// index valid but track number still not set?
|
||||
@ -143,16 +137,13 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
|
||||
if (TrackNumber == FFMS_TRACKMASK_ALL)
|
||||
TrackNumber = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, &ErrInfo);
|
||||
}
|
||||
else {
|
||||
else
|
||||
FFMS_CancelIndexing(Indexer);
|
||||
}
|
||||
|
||||
// update access time of index file so it won't get cleaned away
|
||||
if (!wxFileName(CacheName).Touch()) {
|
||||
// warn user?
|
||||
}
|
||||
agi::fs::Touch(CacheName);
|
||||
|
||||
AudioSource = FFMS_CreateAudioSource(FileNameShort.utf8_str(), TrackNumber, Index, -1, &ErrInfo);
|
||||
AudioSource = FFMS_CreateAudioSource(filename.string().c_str(), TrackNumber, Index, -1, &ErrInfo);
|
||||
if (!AudioSource)
|
||||
throw agi::AudioProviderOpenError(std::string("Failed to open audio track: ") + ErrInfo.Buffer, 0);
|
||||
|
||||
@ -192,8 +183,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) {
|
||||
}
|
||||
|
||||
void FFmpegSourceAudioProvider::FillBuffer(void *Buf, int64_t Start, int64_t Count) const {
|
||||
if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo)) {
|
||||
if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo))
|
||||
throw AudioDecodeError(std::string("Failed to get audio samples: ") + ErrInfo.Buffer);
|
||||
}
|
||||
}
|
||||
#endif /* WITH_FFMS2 */
|
||||
|
@ -34,8 +34,8 @@
|
||||
|
||||
#ifdef WITH_FFMS2
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
#include "ffmpegsource_common.h"
|
||||
|
||||
#include "ffmpegsource_common.h"
|
||||
|
||||
/// @class FFmpegSourceAudioProvider
|
||||
/// @brief Implements audio loading with the FFMS library.
|
||||
@ -46,11 +46,11 @@ class FFmpegSourceAudioProvider : public AudioProvider, FFmpegSourceProvider {
|
||||
mutable char FFMSErrMsg[1024]; ///< FFMS error message
|
||||
mutable FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
|
||||
|
||||
void LoadAudio(wxString filename);
|
||||
void LoadAudio(agi::fs::path const& filename);
|
||||
void FillBuffer(void *buf, int64_t start, int64_t count) const;
|
||||
|
||||
public:
|
||||
FFmpegSourceAudioProvider(wxString filename);
|
||||
FFmpegSourceAudioProvider(agi::fs::path const& filename);
|
||||
|
||||
/// @brief Checks sample endianness
|
||||
/// @return Returns true.
|
||||
|
@ -36,50 +36,43 @@
|
||||
|
||||
#include "audio_provider_hd.h"
|
||||
|
||||
#include <wx/filefn.h>
|
||||
#include <wx/filename.h>
|
||||
|
||||
#include <libaegisub/background_runner.h>
|
||||
#include <libaegisub/io.h>
|
||||
|
||||
#include "audio_controller.h"
|
||||
#include "audio_provider_pcm.h"
|
||||
#include "compat.h"
|
||||
#include "options.h"
|
||||
#include "standard_paths.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace {
|
||||
wxString cache_dir() {
|
||||
wxString path = to_wx(OPT_GET("Audio/Cache/HD/Location")->GetString());
|
||||
if (path == "default")
|
||||
path = "?temp/";
|
||||
#include <libaegisub/access.h>
|
||||
#include <libaegisub/background_runner.h>
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/io.h>
|
||||
#include <libaegisub/path.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
return DecodeRelativePath(StandardPaths::DecodePath(path), StandardPaths::DecodePath("?user/"));
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace {
|
||||
agi::fs::path cache_dir() {
|
||||
std::string path = OPT_GET("Audio/Cache/HD/Location")->GetString();
|
||||
if (path == "default")
|
||||
path = "?temp";
|
||||
|
||||
return config::path->MakeAbsolute(config::path->Decode(path), "?temp");
|
||||
}
|
||||
|
||||
wxString cache_path() {
|
||||
wxString pattern = to_wx(OPT_GET("Audio/Cache/HD/Name")->GetString());
|
||||
if (pattern.Find("%02i") == wxNOT_FOUND) pattern = "audio%02i.tmp";
|
||||
|
||||
// Try from 00 to 99
|
||||
for (int i=0;i<100;i++) {
|
||||
// File exists?
|
||||
wxFileName curNameTry(cache_dir(), wxString::Format(pattern, i));
|
||||
#if wxCHECK_VERSION(2, 9, 4)
|
||||
if (!curNameTry.Exists())
|
||||
#else
|
||||
if (!curNameTry.FileExists() && !curNameTry.DirExists())
|
||||
#endif
|
||||
return curNameTry.GetFullPath();
|
||||
}
|
||||
return "";
|
||||
agi::fs::path cache_path() {
|
||||
std::string pattern = OPT_GET("Audio/Cache/HD/Name")->GetString();
|
||||
if (!boost::contains(pattern, "%02i")) pattern = "audio%02i.tmp";
|
||||
boost::replace_all(pattern, "%02i", "%%%%-%%%%-%%%%-%%%%");
|
||||
return unique_path(cache_dir()/pattern);
|
||||
}
|
||||
|
||||
/// A PCM audio provider for raw dumps with no header
|
||||
class RawAudioProvider : public PCMAudioProvider {
|
||||
public:
|
||||
RawAudioProvider(wxString const& cache_filename, AudioProvider *src)
|
||||
RawAudioProvider(agi::fs::path const& cache_filename, AudioProvider *src)
|
||||
: PCMAudioProvider(cache_filename)
|
||||
{
|
||||
bytes_per_sample = src->GetBytesPerSample();
|
||||
@ -110,30 +103,27 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::BackgroundRunner *br)
|
||||
float_samples = source->AreSamplesFloat();
|
||||
|
||||
// Check free space
|
||||
wxDiskspaceSize_t freespace;
|
||||
if (wxGetDiskSpace(cache_dir(), 0, &freespace)) {
|
||||
if (num_samples * channels * bytes_per_sample > freespace)
|
||||
throw agi::AudioCacheOpenError("Not enough free disk space in " + from_wx(cache_dir()) + " to cache the audio", 0);
|
||||
}
|
||||
if ((uint64_t)num_samples * channels * bytes_per_sample > agi::fs::FreeSpace(cache_dir()))
|
||||
throw agi::AudioCacheOpenError("Not enough free disk space in " + cache_dir().string() + " to cache the audio", 0);
|
||||
|
||||
diskCacheFilename = cache_path();
|
||||
|
||||
try {
|
||||
{
|
||||
agi::io::Save out(from_wx(diskCacheFilename), true);
|
||||
agi::io::Save out(diskCacheFilename, true);
|
||||
br->Run(bind(&HDAudioProvider::FillCache, this, src, &out.Get(), std::placeholders::_1));
|
||||
}
|
||||
cache_provider.reset(new RawAudioProvider(diskCacheFilename, src));
|
||||
}
|
||||
catch (...) {
|
||||
wxRemoveFile(diskCacheFilename);
|
||||
agi::fs::Remove(diskCacheFilename);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
HDAudioProvider::~HDAudioProvider() {
|
||||
cache_provider.reset(); // explicitly close the file so we can delete it
|
||||
wxRemoveFile(diskCacheFilename);
|
||||
agi::fs::Remove(diskCacheFilename);
|
||||
}
|
||||
|
||||
void HDAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
|
||||
|
@ -32,8 +32,6 @@
|
||||
/// @ingroup audio_input
|
||||
///
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
@ -45,7 +43,7 @@ namespace agi {
|
||||
|
||||
class HDAudioProvider : public AudioProvider {
|
||||
/// Name of the file which the decoded audio is written to
|
||||
wxString diskCacheFilename;
|
||||
agi::fs::path diskCacheFilename;
|
||||
/// Audio provider which reads from the decoded cache
|
||||
agi::scoped_ptr<AudioProvider> cache_provider;
|
||||
|
||||
|
@ -29,6 +29,6 @@ LockAudioProvider::LockAudioProvider(AudioProvider *source) : source(source) {
|
||||
}
|
||||
|
||||
void LockAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
|
||||
wxMutexLocker lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
source->GetAudio(buf, start, count);
|
||||
}
|
||||
|
@ -18,13 +18,12 @@
|
||||
|
||||
#include "include/aegisub/audio_provider.h"
|
||||
|
||||
#include <libaegisub/scoped_ptr.h>
|
||||
|
||||
#include <wx/thread.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class LockAudioProvider : public AudioProvider {
|
||||
agi::scoped_ptr<const AudioProvider> source;
|
||||
mutable wxMutex mutex;
|
||||
std::unique_ptr<const AudioProvider> source;
|
||||
mutable std::mutex mutex;
|
||||
|
||||
void FillBuffer(void *buf, int64_t start, int64_t count) const;
|
||||
public:
|
||||
|
@ -32,30 +32,26 @@
|
||||
/// @ingroup audio_input
|
||||
///
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "audio_provider_pcm.h"
|
||||
|
||||
#include "aegisub_endian.h"
|
||||
#include "audio_controller.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#ifndef __WINDOWS__
|
||||
#ifndef _WIN32
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include <wx/file.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <libaegisub/log.h>
|
||||
|
||||
#include "aegisub_endian.h"
|
||||
#include "audio_controller.h"
|
||||
#include "audio_provider_pcm.h"
|
||||
#include "compat.h"
|
||||
#include "utils.h"
|
||||
|
||||
PCMAudioProvider::PCMAudioProvider(const wxString &filename)
|
||||
PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename)
|
||||
: current_mapping(0)
|
||||
, mapping_start(0)
|
||||
, mapping_length(0)
|
||||
@ -73,13 +69,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
|
||||
0);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
throw agi::FileNotFoundError(from_wx(filename));
|
||||
|
||||
LARGE_INTEGER li_file_size = {0};
|
||||
if (!GetFileSizeEx(file_handle, &li_file_size))
|
||||
throw agi::AudioProviderOpenError("Failed getting file size", 0);
|
||||
|
||||
file_size = li_file_size.QuadPart;
|
||||
throw agi::fs::FileNotFound(filename);
|
||||
|
||||
file_mapping = CreateFileMapping(
|
||||
file_handle,
|
||||
@ -91,24 +81,22 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename)
|
||||
if (file_mapping == 0)
|
||||
throw agi::AudioProviderOpenError("Failed creating file mapping", 0);
|
||||
#else
|
||||
, file_handle(open(filename.mb_str(*wxConvFileName), O_RDONLY), close)
|
||||
, file_handle(open(filename.c_str(), O_RDONLY), close)
|
||||
{
|
||||
if (file_handle == -1)
|
||||
throw agi::FileNotFoundError(from_wx(filename));
|
||||
|
||||
struct stat filestats;
|
||||
memset(&filestats, 0, sizeof(filestats));
|
||||
if (fstat(file_handle, &filestats)) {
|
||||
close(file_handle);
|
||||
throw agi::AudioProviderOpenError("Could not stat file to get size", 0);
|
||||
}
|
||||
file_size = filestats.st_size;
|
||||
throw agi::fs::FileNotFound(filename.string());
|
||||
#endif
|
||||
float_samples = false;
|
||||
|
||||
try {
|
||||
file_size = agi::fs::Size(filename);
|
||||
}
|
||||
catch (agi::Exception const& e) {
|
||||
throw agi::AudioPlayerOpenError("Could not get file size", e.Copy());
|
||||
}
|
||||
}
|
||||
|
||||
PCMAudioProvider::~PCMAudioProvider()
|
||||
{
|
||||
PCMAudioProvider::~PCMAudioProvider() {
|
||||
#ifdef _WIN32
|
||||
if (current_mapping)
|
||||
UnmapViewOfFile(current_mapping);
|
||||
@ -118,11 +106,9 @@ PCMAudioProvider::~PCMAudioProvider()
|
||||
#endif
|
||||
}
|
||||
|
||||
char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const
|
||||
{
|
||||
if (range_start + range_length > file_size) {
|
||||
char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const {
|
||||
if (range_start + range_length > file_size)
|
||||
throw AudioDecodeError("Attempted to map beyond end of file");
|
||||
}
|
||||
|
||||
// Check whether the requested range is already visible
|
||||
if (!current_mapping || range_start < mapping_start || range_start+range_length > mapping_start+(int64_t)mapping_length) {
|
||||
@ -171,9 +157,8 @@ char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t rang
|
||||
current_mapping = mmap(0, mapping_length, PROT_READ, MAP_PRIVATE, file_handle, mapping_start);
|
||||
#endif
|
||||
|
||||
if (!current_mapping) {
|
||||
if (!current_mapping)
|
||||
throw AudioDecodeError("Failed mapping a view of the file");
|
||||
}
|
||||
}
|
||||
|
||||
assert(current_mapping);
|
||||
@ -186,8 +171,7 @@ char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t rang
|
||||
return ((char*)current_mapping) + rel_ofs;
|
||||
}
|
||||
|
||||
void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const
|
||||
{
|
||||
void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
|
||||
// Read blocks from the file
|
||||
size_t index = 0;
|
||||
while (count > 0 && index < index_points.size()) {
|
||||
@ -268,10 +252,10 @@ class RiffWavPCMAudioProvider : public PCMAudioProvider {
|
||||
|
||||
public:
|
||||
|
||||
RiffWavPCMAudioProvider(const wxString &_filename)
|
||||
: PCMAudioProvider(_filename)
|
||||
RiffWavPCMAudioProvider(agi::fs::path const& filename)
|
||||
: PCMAudioProvider(filename)
|
||||
{
|
||||
filename = _filename;
|
||||
this->filename = filename;
|
||||
|
||||
// Read header
|
||||
void *filestart = EnsureRangeAccessible(0, sizeof(RIFFChunk));
|
||||
@ -345,8 +329,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool AreSamplesNativeEndian() const
|
||||
{
|
||||
bool AreSamplesNativeEndian() const {
|
||||
// 8 bit samples don't consider endianness
|
||||
if (bytes_per_sample < 2) return true;
|
||||
// Otherwise test whether we're little endian
|
||||
@ -411,17 +394,16 @@ class Wave64AudioProvider : public PCMAudioProvider {
|
||||
uint64_t chunk_size;
|
||||
};
|
||||
|
||||
inline bool CheckGuid(const uint8_t *guid1, const uint8_t *guid2)
|
||||
{
|
||||
bool CheckGuid(const uint8_t *guid1, const uint8_t *guid2) {
|
||||
return memcmp(guid1, guid2, 16) == 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Wave64AudioProvider(const wxString &_filename)
|
||||
: PCMAudioProvider(_filename)
|
||||
Wave64AudioProvider(agi::fs::path const& filename)
|
||||
: PCMAudioProvider(filename)
|
||||
{
|
||||
filename = _filename;
|
||||
this->filename = filename;
|
||||
|
||||
int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk);
|
||||
|
||||
@ -496,8 +478,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool AreSamplesNativeEndian() const
|
||||
{
|
||||
bool AreSamplesNativeEndian() const {
|
||||
// 8 bit samples don't consider endianness
|
||||
if (bytes_per_sample < 2) return true;
|
||||
// Otherwise test whether we're little endian
|
||||
@ -506,8 +487,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
AudioProvider *CreatePCMAudioProvider(const wxString &filename)
|
||||
{
|
||||
AudioProvider *CreatePCMAudioProvider(agi::fs::path const& filename) {
|
||||
bool wrong_file_type = true;
|
||||
std::string msg;
|
||||
|
||||
|
@ -34,9 +34,6 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <wx/file.h>
|
||||
#include <wx/thread.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
@ -63,7 +60,7 @@ class PCMAudioProvider : public AudioProvider {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
PCMAudioProvider(const wxString &filename); // Create base object and open the file mapping
|
||||
PCMAudioProvider(agi::fs::path const& filename); // Create base object and open the file mapping
|
||||
virtual ~PCMAudioProvider(); // Closes the file mapping
|
||||
char * EnsureRangeAccessible(int64_t range_start, int64_t range_length) const; // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range
|
||||
|
||||
@ -84,4 +81,4 @@ protected:
|
||||
};
|
||||
|
||||
// Construct the right PCM audio provider (if any) for the file
|
||||
AudioProvider *CreatePCMAudioProvider(const wxString &filename);
|
||||
AudioProvider *CreatePCMAudioProvider(agi::fs::path const& filename);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user