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