// Copyright (c) 2005, Rodrigo Braz Monteiro // Copyright (c) 2010, Thomas Goyne // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Aegisub Group nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Aegisub Project http://www.aegisub.org/ /// @file ass_override.cpp /// @brief Parse and modify ASSA style overrides /// @ingroup subs_storage /// #include "ass_dialogue.h" #include "utils.h" #include #include #include #include #include #include #include #include #include using namespace boost::adaptors; AssOverrideParameter::AssOverrideParameter(VariableDataType type, AssParameterClass classification) : type(type) , classification(classification) { } #ifdef _MSC_VER AssOverrideParameter::AssOverrideParameter(AssOverrideParameter&& o) : value(std::move(o.value)) , block(std::move(o.block)) , type(o.type) , classification(o.classification) , omitted(o.omitted) { } AssOverrideParameter& AssOverrideParameter::operator=(AssOverrideParameter&& rhs) { value = std::move(rhs.value); block = std::move(rhs.block); type = rhs.type; classification = rhs.classification; return *this; } #endif AssOverrideParameter::~AssOverrideParameter() { } template<> std::string AssOverrideParameter::Get() const { if (omitted) throw agi::InternalError("AssOverrideParameter::Get() called on omitted parameter", nullptr); if (block.get()) { std::string str(block->GetText()); if (boost::starts_with(str, "{")) str.erase(begin(str)); if (boost::ends_with(str, "}")) str.erase(end(str) - 1); return str; } return value; } template<> int AssOverrideParameter::Get() const { if (classification == AssParameterClass::ALPHA) // &Hxx&, but vsfilter lets you leave everything out return mid(0, strtol(std::find_if(value.c_str(), value.c_str() + value.size(), isxdigit), nullptr, 16), 255); return atoi(Get().c_str()); } template<> double AssOverrideParameter::Get() const { return atof(Get().c_str()); } template<> float AssOverrideParameter::Get() const { return atof(Get().c_str()); } template<> bool AssOverrideParameter::Get() const { return Get() != 0; } template<> agi::Color AssOverrideParameter::Get() const { return Get(); } template<> AssDialogueBlockOverride *AssOverrideParameter::Get() const { if (!block.get()) { block = agi::make_unique(Get()); block->ParseTags(); } return block.get(); } template<> void AssOverrideParameter::Set(std::string new_value) { omitted = false; value = new_value; block.reset(); } template<> void AssOverrideParameter::Set(int new_value) { if (classification == AssParameterClass::ALPHA) Set(agi::format("&H%02X&", mid(0, new_value, 255))); else Set(std::to_string(new_value)); } template<> void AssOverrideParameter::Set(double new_value) { Set(float_to_string(new_value)); } template<> void AssOverrideParameter::Set(bool new_value) { Set(new_value); } namespace { /// The parameter is absent unless the total number of parameters is the /// indicated number. Note that only arguments not at the end need to be marked /// as optional; this is just to know which parameters to skip when there are /// earlier optional arguments enum AssParameterOptional { NOT_OPTIONAL = 0xFF, OPTIONAL_1 = 0x01, OPTIONAL_2 = 0x02, OPTIONAL_3 = 0x04, OPTIONAL_4 = 0x08, OPTIONAL_5 = 0x10, OPTIONAL_6 = 0x20, OPTIONAL_7 = 0x40 }; /// Prototype of a single override parameter struct AssOverrideParamProto { /// ASS_ParameterOptional int optional; /// Type of this parameter VariableDataType type; /// Semantic type of this parameter AssParameterClass classification; }; struct AssOverrideTagProto { /// Name of the tag, with slash std::string name; /// Parameters to this tag std::vector params; typedef std::vector::iterator iterator; /// @brief Add a parameter to this tag prototype /// @param type Data type of the parameter /// @param classi Semantic type of the parameter /// @param opt Situations in which this parameter is present void AddParam(VariableDataType type, AssParameterClass classi = AssParameterClass::NORMAL, int opt = NOT_OPTIONAL) { params.push_back(AssOverrideParamProto{opt, type, classi}); } /// @brief Convenience function for single-argument tags /// @param name Name of the tag, with slash /// @param type Data type of the parameter /// @param classi Semantic type of the parameter /// @param opt Situations in which this parameter is present void Set(const char *name, VariableDataType type, AssParameterClass classi = AssParameterClass::NORMAL, int opt = NOT_OPTIONAL) { this->name = name; params.push_back(AssOverrideParamProto{opt, type, classi}); } }; static std::vector proto; static void load_protos() { static bool loaded = false; if (loaded) return; loaded = true; proto.resize(56); int i = 0; // Longer tag names must appear before shorter tag names proto[0].Set("\\alpha", VariableDataType::TEXT, AssParameterClass::ALPHA); // \alpha&H& proto[++i].Set("\\bord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \bord proto[++i].Set("\\xbord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \xbord proto[++i].Set("\\ybord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \ybord proto[++i].Set("\\shad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \shad proto[++i].Set("\\xshad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \xshad proto[++i].Set("\\yshad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \yshad // \fade(,,,,,,) i++; proto[i].name = "\\fade"; proto[i].AddParam(VariableDataType::INT); proto[i].AddParam(VariableDataType::INT); proto[i].AddParam(VariableDataType::INT); proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START); proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START); proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START); proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START); // \move(,,,[,,]) i++; proto[i].name = "\\move"; proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X); proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y); proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X); proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y); proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START); proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START); // If these are rearranged, keep rect clip and vector clip adjacent in this order // \clip(,,,) i++; proto[i].name = "\\clip"; proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X); proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y); proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X); proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y); // \clip([,]) i++; proto[i].name = "\\clip"; proto[i].AddParam(VariableDataType::INT, AssParameterClass::NORMAL,OPTIONAL_2); proto[i].AddParam(VariableDataType::TEXT, AssParameterClass::DRAWING); // \iclip(,,,) i++; proto[i].name = "\\iclip"; proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X); proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y); proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X); proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y); // \iclip([,]) i++; proto[i].name = "\\iclip"; proto[i].AddParam(VariableDataType::INT, AssParameterClass::NORMAL,OPTIONAL_2); proto[i].AddParam(VariableDataType::TEXT, AssParameterClass::DRAWING); proto[++i].Set("\\fscx", VariableDataType::FLOAT, AssParameterClass::RELATIVE_SIZE_X); // \fscx proto[++i].Set("\\fscy", VariableDataType::FLOAT, AssParameterClass::RELATIVE_SIZE_Y); // \fscy // \pos(,) i++; proto[i].name = "\\pos"; proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X); proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y); // \org(,) i++; proto[i].name = "\\org"; proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X); proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y); proto[++i].Set("\\pbo", VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y); // \pbo // \fad(,) i++; proto[i].name = "\\fad"; proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START); proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_END); proto[++i].Set("\\fsp", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fsp proto[++i].Set("\\frx", VariableDataType::FLOAT); // \frx proto[++i].Set("\\fry", VariableDataType::FLOAT); // \fry proto[++i].Set("\\frz", VariableDataType::FLOAT); // \frz proto[++i].Set("\\fr", VariableDataType::FLOAT); // \fr proto[++i].Set("\\fax", VariableDataType::FLOAT); // \fax proto[++i].Set("\\fay", VariableDataType::FLOAT); // \fay proto[++i].Set("\\1c", VariableDataType::TEXT, AssParameterClass::COLOR); // \1c&H& proto[++i].Set("\\2c", VariableDataType::TEXT, AssParameterClass::COLOR); // \2c&H& proto[++i].Set("\\3c", VariableDataType::TEXT, AssParameterClass::COLOR); // \3c&H& proto[++i].Set("\\4c", VariableDataType::TEXT, AssParameterClass::COLOR); // \4c&H& proto[++i].Set("\\1a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \1a&H& proto[++i].Set("\\2a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \2a&H& proto[++i].Set("\\3a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \3a&H& proto[++i].Set("\\4a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \4a&H& proto[++i].Set("\\fe", VariableDataType::TEXT); // \fe proto[++i].Set("\\ko", VariableDataType::INT, AssParameterClass::KARAOKE); // \ko proto[++i].Set("\\kf", VariableDataType::INT, AssParameterClass::KARAOKE); // \kf proto[++i].Set("\\be", VariableDataType::INT, AssParameterClass::ABSOLUTE_SIZE); // \be proto[++i].Set("\\blur", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \blur proto[++i].Set("\\fn", VariableDataType::TEXT); // \fn proto[++i].Set("\\fs+", VariableDataType::FLOAT); // \fs+ proto[++i].Set("\\fs-", VariableDataType::FLOAT); // \fs- proto[++i].Set("\\fs", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fs proto[++i].Set("\\an", VariableDataType::INT); // \an proto[++i].Set("\\c", VariableDataType::TEXT, AssParameterClass::COLOR); // \c&H& proto[++i].Set("\\b", VariableDataType::INT); // \b<0/1/weight> proto[++i].Set("\\i", VariableDataType::BOOL); // \i<0/1> proto[++i].Set("\\u", VariableDataType::BOOL); // \u<0/1> proto[++i].Set("\\s", VariableDataType::BOOL); // \s<0/1> proto[++i].Set("\\a", VariableDataType::INT); // \a proto[++i].Set("\\k", VariableDataType::INT, AssParameterClass::KARAOKE); // \k proto[++i].Set("\\K", VariableDataType::INT, AssParameterClass::KARAOKE); // \K proto[++i].Set("\\q", VariableDataType::INT); // \q<0-3> proto[++i].Set("\\p", VariableDataType::INT); // \p proto[++i].Set("\\r", VariableDataType::TEXT); // \r[] // \t([,,][,]