Do disgusting things to make LuaAssFile work
LuaAssFile relied on the lifetime guarantees of intrusive lists (i.e. you can pretty much do whatever the fuck you want and it'll work out), but AssInfo objects are no longer owned by an intrusive list. Do awful things to lazily copy the header section when needed to provide similar lifetime guarantees.
This commit is contained in:
parent
f6463f0fd4
commit
8ee3095506
2 changed files with 101 additions and 39 deletions
|
@ -73,19 +73,34 @@ namespace Automation4 {
|
|||
void CheckBounds(int idx);
|
||||
|
||||
/// How ass file been modified by the script since the last commit
|
||||
int modification_type;
|
||||
int modification_type = 0;
|
||||
|
||||
/// Reference count used to avoid deleting this until both lua and the
|
||||
/// calling C++ code are done with it
|
||||
int references;
|
||||
int references = 2;
|
||||
|
||||
/// Set of subtitle lines being modified; initially a shallow copy of ass->Line
|
||||
std::vector<AssEntry*> lines;
|
||||
bool script_info_copied = false;
|
||||
|
||||
/// Commits to apply once processing completes successfully
|
||||
std::deque<PendingCommit> pending_commits;
|
||||
/// Lines to delete once processing complete successfully
|
||||
std::vector<std::unique_ptr<AssEntry>> lines_to_delete;
|
||||
|
||||
/// Create copies of all of the lines in the script info section if it
|
||||
/// hasn't already happened. This is done lazily, since it only needs
|
||||
/// to happen when the user modifies the headers in some way, which
|
||||
/// most runs of a script will not do.
|
||||
void InitScriptInfoIfNeeded();
|
||||
/// Add the line at the given index to the list of lines to be deleted
|
||||
/// when the script completes, unless it's an AssInfo, since those are
|
||||
/// owned by the container.
|
||||
void QueueLineForDeletion(size_t idx);
|
||||
/// Set the line at the index to the given value
|
||||
void AssignLine(size_t idx, std::unique_ptr<AssEntry> e);
|
||||
void InsertLine(std::vector<AssEntry *> &vec, size_t idx, std::unique_ptr<AssEntry> e);
|
||||
|
||||
int ObjectIndexRead(lua_State *L);
|
||||
void ObjectIndexWrite(lua_State *L);
|
||||
int ObjectGetLen(lua_State *L);
|
||||
|
@ -107,7 +122,7 @@ namespace Automation4 {
|
|||
static LuaAssFile *GetObjPointer(lua_State *L, int idx);
|
||||
|
||||
/// makes a Lua representation of AssEntry and places on the top of the stack
|
||||
static void AssEntryToLua(lua_State *L, AssEntry *e);
|
||||
void AssEntryToLua(lua_State *L, size_t idx);
|
||||
/// assumes a Lua representation of AssEntry on the top of the stack, and creates an AssEntry object of it
|
||||
static std::unique_ptr<AssEntry> LuaToAssEntry(lua_State *L);
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include "auto4_lua.h"
|
||||
|
||||
#include "auto4_lua_utils.h"
|
||||
#include "ass_attachment.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_info.h"
|
||||
#include "ass_file.h"
|
||||
|
@ -50,8 +49,6 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/range/adaptor/indirected.hpp>
|
||||
#include <boost/range/algorithm_ext.hpp>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
|
@ -118,8 +115,8 @@ namespace {
|
|||
|
||||
int modification_mask(AssEntry *e)
|
||||
{
|
||||
switch (e->Group())
|
||||
{
|
||||
if (!e) return AssFile::COMMIT_SCRIPTINFO;
|
||||
switch (e->Group()) {
|
||||
case AssEntryGroup::DIALOGUE: return AssFile::COMMIT_DIAG_ADDREM;
|
||||
case AssEntryGroup::STYLE: return AssFile::COMMIT_STYLES;
|
||||
default: return AssFile::COMMIT_SCRIPTINFO;
|
||||
|
@ -142,19 +139,23 @@ namespace Automation4 {
|
|||
luaL_error(L, "Requested out-of-range line from subtitle file: %d", idx);
|
||||
}
|
||||
|
||||
void LuaAssFile::AssEntryToLua(lua_State *L, AssEntry *e)
|
||||
void LuaAssFile::AssEntryToLua(lua_State *L, size_t idx)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
const AssEntry *e = lines[idx];
|
||||
if (!e)
|
||||
e = &ass->Info[idx];
|
||||
|
||||
set_field(L, "section", e->GroupHeader());
|
||||
|
||||
if (AssInfo *info = dynamic_cast<AssInfo*>(e)) {
|
||||
if (auto info = dynamic_cast<const AssInfo*>(e)) {
|
||||
set_field(L, "raw", info->GetEntryData());
|
||||
set_field(L, "key", info->Key());
|
||||
set_field(L, "value", info->Value());
|
||||
set_field(L, "class", "info");
|
||||
}
|
||||
else if (AssDialogue *dia = dynamic_cast<AssDialogue*>(e)) {
|
||||
else if (auto dia = dynamic_cast<const AssDialogue*>(e)) {
|
||||
set_field(L, "raw", dia->GetEntryData());
|
||||
set_field(L, "comment", dia->Comment);
|
||||
|
||||
|
@ -176,7 +177,7 @@ namespace Automation4 {
|
|||
|
||||
set_field(L, "class", "dialogue");
|
||||
}
|
||||
else if (AssStyle *sty = dynamic_cast<AssStyle*>(e)) {
|
||||
else if (auto sty = dynamic_cast<const AssStyle*>(e)) {
|
||||
set_field(L, "raw", sty->GetEntryData());
|
||||
set_field(L, "name", sty->name);
|
||||
|
||||
|
@ -308,7 +309,7 @@ namespace Automation4 {
|
|||
// read an indexed AssEntry
|
||||
int idx = lua_tointeger(L, 2);
|
||||
CheckBounds(idx);
|
||||
AssEntryToLua(L, lines[idx - 1]);
|
||||
AssEntryToLua(L, idx - 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -350,6 +351,52 @@ namespace Automation4 {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void LuaAssFile::InitScriptInfoIfNeeded()
|
||||
{
|
||||
if (script_info_copied) return;
|
||||
size_t i = 0;
|
||||
for (auto const& info : ass->Info) {
|
||||
// Just in case an insane user inserted non-info lines into the
|
||||
// script info section...
|
||||
while (lines[i]) ++i;
|
||||
lines_to_delete.emplace_back(agi::util::make_unique<AssInfo>(info));
|
||||
lines[i++] = lines_to_delete.back().get();
|
||||
}
|
||||
script_info_copied = true;
|
||||
}
|
||||
|
||||
void LuaAssFile::QueueLineForDeletion(size_t idx)
|
||||
{
|
||||
if (!lines[idx] || lines[idx]->Group() == AssEntryGroup::INFO)
|
||||
InitScriptInfoIfNeeded();
|
||||
else
|
||||
lines_to_delete.emplace_back(lines[idx]);
|
||||
}
|
||||
|
||||
void LuaAssFile::AssignLine(size_t idx, std::unique_ptr<AssEntry> e)
|
||||
{
|
||||
auto group = e->Group();
|
||||
if (group == AssEntryGroup::INFO)
|
||||
InitScriptInfoIfNeeded();
|
||||
lines[idx] = e.get();
|
||||
if (group == AssEntryGroup::INFO)
|
||||
lines_to_delete.emplace_back(std::move(e));
|
||||
else
|
||||
e.release();
|
||||
}
|
||||
|
||||
void LuaAssFile::InsertLine(std::vector<AssEntry *> &vec, size_t idx, std::unique_ptr<AssEntry> e)
|
||||
{
|
||||
auto group = e->Group();
|
||||
if (group == AssEntryGroup::INFO)
|
||||
InitScriptInfoIfNeeded();
|
||||
vec.insert(vec.begin() + idx, e.get());
|
||||
if (group == AssEntryGroup::INFO)
|
||||
lines_to_delete.emplace_back(std::move(e));
|
||||
else
|
||||
e.release();
|
||||
}
|
||||
|
||||
void LuaAssFile::ObjectIndexWrite(lua_State *L)
|
||||
{
|
||||
// instead of implementing everything twice, just call the other modification-functions from here
|
||||
|
@ -375,11 +422,12 @@ namespace Automation4 {
|
|||
// replace line at index n or delete
|
||||
if (!lua_isnil(L, 3)) {
|
||||
// insert
|
||||
CheckBounds(n);
|
||||
|
||||
auto e = LuaToAssEntry(L);
|
||||
modification_type |= modification_mask(e.get());
|
||||
CheckBounds(n);
|
||||
lines_to_delete.emplace_back(lines[n - 1]);
|
||||
lines[n - 1] = e.release();
|
||||
QueueLineForDeletion(n - 1);
|
||||
AssignLine(n - 1, std::move(e));
|
||||
}
|
||||
else {
|
||||
// delete
|
||||
|
@ -418,7 +466,7 @@ namespace Automation4 {
|
|||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
if (id_idx < ids.size() && ids[id_idx] == i) {
|
||||
modification_type |= modification_mask(lines[i]);
|
||||
lines_to_delete.emplace_back(lines[i]);
|
||||
QueueLineForDeletion(i);
|
||||
++id_idx;
|
||||
}
|
||||
else {
|
||||
|
@ -440,7 +488,7 @@ namespace Automation4 {
|
|||
|
||||
for (size_t i = a; i < b; ++i) {
|
||||
modification_type |= modification_mask(lines[i]);
|
||||
lines_to_delete.emplace_back(lines[i]);
|
||||
QueueLineForDeletion(i);
|
||||
}
|
||||
|
||||
lines.erase(lines.begin() + a, lines.begin() + b);
|
||||
|
@ -457,24 +505,23 @@ namespace Automation4 {
|
|||
auto e = LuaToAssEntry(L);
|
||||
modification_type |= modification_mask(e.get());
|
||||
|
||||
// Find the appropriate place to put it
|
||||
auto it = lines.end();
|
||||
if (!lines.empty()) {
|
||||
do {
|
||||
--it;
|
||||
}
|
||||
while (it != lines.begin() && (*it)->Group() != e->Group());
|
||||
if (lines.empty()) {
|
||||
InsertLine(lines, 0, std::move(e));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it == lines.end() || (*it)->Group() != e->Group()) {
|
||||
// The new entry belongs to a group that doesn't exist yet, so
|
||||
// create it at the end of the file
|
||||
lines.push_back(e.release());
|
||||
// Find the appropriate place to put it
|
||||
auto group = e->Group();
|
||||
for (size_t i = lines.size(); i > 0; --i) {
|
||||
auto cur_group = lines[i - 1] ? lines[i - 1]->Group() : AssEntryGroup::INFO;
|
||||
if (cur_group == group) {
|
||||
InsertLine(lines, i, std::move(e));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Append the entry to the end of the existing group
|
||||
lines.insert(++it, e.release());
|
||||
}
|
||||
|
||||
// No lines of this type exist already, so just append it to the end
|
||||
if (e) InsertLine(lines, lines.size(), std::move(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,7 +547,7 @@ namespace Automation4 {
|
|||
lua_pushvalue(L, i);
|
||||
auto e = LuaToAssEntry(L);
|
||||
modification_type |= modification_mask(e.get());
|
||||
new_entries[i - 2] = e.release();
|
||||
InsertLine(new_entries, i - 2, std::move(e));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lines.insert(lines.begin() + before - 1, new_entries.begin(), new_entries.end());
|
||||
|
@ -531,7 +578,7 @@ namespace Automation4 {
|
|||
}
|
||||
|
||||
push_value(L, i + 1);
|
||||
AssEntryToLua(L, lines[i]);
|
||||
AssEntryToLua(L, i);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
@ -596,11 +643,13 @@ namespace Automation4 {
|
|||
std::vector<AssEntry *> LuaAssFile::ProcessingComplete(wxString const& undo_description)
|
||||
{
|
||||
auto apply_lines = [&](std::vector<AssEntry *> const& lines) {
|
||||
if (script_info_copied)
|
||||
ass->Info.clear();
|
||||
ass->Styles.clear();
|
||||
ass->Events.clear();
|
||||
|
||||
for (auto line : lines) {
|
||||
if (!line) continue;
|
||||
switch (line->Group()) {
|
||||
case AssEntryGroup::INFO: ass->Info.push_back(*static_cast<AssInfo *>(line)); break;
|
||||
case AssEntryGroup::STYLE: ass->Styles.push_back(*static_cast<AssStyle *>(line)); break;
|
||||
|
@ -641,11 +690,9 @@ namespace Automation4 {
|
|||
, L(L)
|
||||
, can_modify(can_modify)
|
||||
, can_set_undo(can_set_undo)
|
||||
, modification_type(0)
|
||||
, references(2)
|
||||
{
|
||||
for (auto& line : ass->Info)
|
||||
lines.push_back(&line);
|
||||
lines.push_back(nullptr);
|
||||
for (auto& line : ass->Styles)
|
||||
lines.push_back(&line);
|
||||
for (auto& line : ass->Events)
|
||||
|
|
Loading…
Reference in a new issue