/* * Copyright (C) 2004-2005 Vadim Berezniker * http://www.kryptolus.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "common.h" #include "sabbu.h" #include "sound.h" #include "kryTextFileReader.h" #include "krySubSSA.h" #include "krySubReader.h" #include "krySubWriter.h" #include "krySubWriterSSA.h" extern struct krySubSSAColumnInfo *sub_ssa_save_ssa_columns; extern struct sabbu app; /* * Calculates the width of the given piece of data. * Uses the information within the column info structure to retreive the value that would * be written out, and calculates its width. */ int sub_ssa_save_calculate_item_width(struct krySubSSAColumnInfo *info, void *data) { int len = 0; // we don't support transforms for doubles, so we just calculate the width of the returned value if(info->type == TOKEN_DOUBLE) { // the width is the width of the integer part plus the decimal count (specified in the type_param field) // plus one if the decimal count is not zero (for the decimal point) double val_dbl = ((SubSsaSaveColumnFuncDouble)info->func_write)(data); return (integer_calculate_width(int(val_dbl)) + info->type_param + (info->type_param ? 1 : 0)); } void *val = info->func_write(data); // if this column uses a string transform (type -> string), we transform the data and return the width of the string if(info->transform_type == TRANSFORM_TO_STRING && info->func_transform_write) { char *str = (char *) info->func_transform_write(val); len = strlen(str); kry_free(str); return len; } // check if there's a direct (transform to same type) if(info->transform_type == TRANSFORM_DIRECT && info->func_transform_write) val = info->func_transform_write(val); if(info->type == TOKEN_STR) len = val == NULL ? 0 : (strlen((char *) val)); else if(info->type == TOKEN_INT) len = integer_calculate_width((long) val); return len; } // allocates space for temporary data in the column array structure void sub_ssa_save_allocate_temp(GList *columns, kryScript *script) { for(GList *ptr = columns; ptr; ptr = ptr->next) { struct krySubSSAColumnInfo *column_info = (struct krySubSSAColumnInfo *) ptr->data;; column_info->data = kry_new0(sub_ssa_save_column_temp_info); } } // frees space used for temporary data in the column array structure void sub_ssa_save_free_temp(GList *columns, kryScript *script) { for(GList *ptr = columns; ptr; ptr = ptr->next) { struct krySubSSAColumnInfo *column_info = (struct krySubSSAColumnInfo *) ptr->data; struct sub_ssa_save_column_temp_info *temp_info = (struct sub_ssa_save_column_temp_info *) column_info->data; if(temp_info->format) kry_free(temp_info->format); kry_free(column_info->data); column_info->data = NULL; } } // computes the format specifiers that will be used to write data void krySubWriterSSA::ComputeColumnFormats(GList *columns) { for(GList *ptr = columns; ptr; ptr = ptr->next) { struct krySubSSAColumnInfo *column_info = (struct krySubSSAColumnInfo *) ptr->data; struct sub_ssa_save_column_temp_info *temp_info = (struct sub_ssa_save_column_temp_info *)column_info->data; char *type_spec = NULL; if(column_info->transform_type == TRANSFORM_TO_STRING || column_info->type == TOKEN_STR || column_info->type == TOKEN_STR_END || column_info->type == TOKEN_DOUBLE && column_info->type_param) { type_spec = "s"; } else if(column_info->type == TOKEN_INT) { type_spec = "d"; } else if(column_info->type == TOKEN_DOUBLE) { type_spec = "f"; } else { g_warning("unknown format type"); } if(this->m_format_script) { if(!strcmp(type_spec, "s") || !strcmp(type_spec, "d")) { temp_info->format = kry_strdup_printf(KRY_LOC "%%%d%s", temp_info->width + (ptr != columns), type_spec); } else if(!strcmp(type_spec, "f")) { temp_info->format = kry_strdup_printf(KRY_LOC "%%%dd", temp_info->width + (ptr != columns)); } } else { if(!strcmp(type_spec, "s") || !strcmp(type_spec, "d")) { temp_info->format = kry_strdup_printf(KRY_LOC "%%%s", type_spec); } else { temp_info->format = kry_strdup_printf(KRY_LOC "%%d"); } } } } // computes the maximum width of every column based on the header and the data void krySubWriterSSA::ComputeColumnWidths(GList *columns, void *data) { for(GList *ptr = columns; ptr; ptr = ptr->next) { int len = 0; struct krySubSSAColumnInfo *column_info = (struct krySubSSAColumnInfo *) ptr->data; struct sub_ssa_save_column_temp_info *temp_info = (struct sub_ssa_save_column_temp_info *)column_info->data; if(temp_info->width == 0) temp_info->width = strlen(column_info->name); len = sub_ssa_save_calculate_item_width(column_info, data); if(len > temp_info->width) temp_info->width = len; } } void krySubWriterSSA::ComputeColumnWidthsStyles(GList *columns, kryList *styles) { if(!this->m_format_script) return; kryListIterator style_iter; styles->GetIterator(&style_iter); while(kryStyle *style = style_iter.GetNext()) this->ComputeColumnWidths(columns, style); } void krySubWriterSSA::ComputeColumnWidthsEvents(GList *columns, kryList *events) { if(!this->m_format_script) return; kryListIterator event_iter; events->GetIterator(&event_iter); while(kryEvent *event = event_iter.GetNext()) this->ComputeColumnWidths(columns, event); } void krySubWriterSSA::WriteColumnHeaders(GList *columns) { char *buffer; for(GList *ptr = columns; ptr; ptr = ptr->next) { struct krySubSSAColumnInfo *column_info = (struct krySubSSAColumnInfo *) ptr->data; struct sub_ssa_save_column_temp_info *temp_info = (struct sub_ssa_save_column_temp_info *) column_info->data; if(this->m_format_script) { char *buffer_format = kry_strdup_printf(KRY_LOC "%%%ds", temp_info->width); buffer = kry_strdup_printf(KRY_LOC buffer_format, column_info->name); kry_free(buffer_format); } else { buffer = kry_strdup_printf(KRY_LOC "%s", column_info->name); } if(this->m_format_csv) { char *buffer_new = kry_strdup_printf(KRY_LOC "\"%s\"", buffer); kry_free(buffer); buffer = buffer_new; } this->WriteString(buffer); kry_free(buffer); if(column_info->separator) { this->WriteString(column_info->separator); if(!this->m_format_csv) this->WriteString(" "); } } this->WriteString("\n"); } void krySubWriterSSA::WriteRowData(GList *columns, void *data) { char *buffer; for(GList *ptr = columns; ptr; ptr = ptr->next) { void *val_void = NULL, *val = NULL; double val_dbl = 0; struct krySubSSAColumnInfo *column_info = (struct krySubSSAColumnInfo *) ptr->data; struct sub_ssa_save_column_temp_info *temp_info = (struct sub_ssa_save_column_temp_info *) column_info->data; if(column_info->type == TOKEN_DOUBLE) { val_dbl = ((SubSsaSaveColumnFuncDouble)column_info->func_write)(data); val_void = NULL; val = NULL; } else { val = column_info->func_write(data); if(column_info->transform_type != TRANSFORM_NONE && column_info->func_transform_write) val_void = column_info->func_transform_write(val); else val_void = val; } if(column_info->type == TOKEN_DOUBLE) { int integer = (int) val_dbl; if(column_info->type_param) // format is "%s" { char *format = kry_strdup_printf(KRY_LOC "%%s%%d.%%0%dd", column_info->type_param); char *dblstr = kry_strdup_printf(KRY_LOC format, (val_dbl < 0 ? "-" : ""), integer, (int) ((fabs(val_dbl) * pow(10, column_info->type_param)) - (integer * pow(10, column_info->type_param)))); buffer = kry_strdup_printf(KRY_LOC temp_info->format, dblstr); kry_free(dblstr); kry_free(format); } else { buffer = kry_strdup_printf(KRY_LOC temp_info->format, integer); } } else { buffer = kry_strdup_printf(KRY_LOC temp_info->format, val_void); } if(this->m_format_csv) { char *buffer_new = kry_strdup_printf(KRY_LOC "\"%s\"", buffer); kry_free(buffer); buffer = buffer_new; } this->WriteString(buffer); kry_free(buffer); if(column_info->transform_type == TRANSFORM_TO_STRING && column_info->func_transform_write) kry_free(val_void); if(column_info->separator) this->WriteString(column_info->separator); } this->WriteString("\n"); } void krySubWriterSSA::WriteRowDataEvents(GList *columns) { kryScript *script = this->GetScript(); kryHash *style_iconv_map = new kryHash(g_str_hash, g_str_equal, kry_free_minimal, (GDestroyNotify) sabbu_style_iconv_map_free); kryEvent *event_last = NULL; struct style_iconv_map *map; while(kryEventDetailed *event = this->GetNextEvent()) { char *orig_text = NULL; if(event_last && event_last == event) break; sabbu_iconv_get_mapping(script, event, style_iconv_map, &map, TRUE); if(map->present && script->GetEncoding() == ENCODING_ASCII) { #ifdef ICONV_CONST_INBUFFER const char *in_buffer = event->GetText(); #else char *in_buffer = event->GetText(); #endif char *out_buffer = (char *) kry_malloc(strlen(in_buffer) * 2 + 1); char *out_buffer_orig = out_buffer; size_t in_bytes_left = strlen(in_buffer) + 1, out_bytes_left = strlen(in_buffer) * 2 + 1; size_t rv = iconv(map->iconv_h, &in_buffer, &in_bytes_left, &out_buffer, &out_bytes_left); if(rv == 0) { orig_text = kry_strdup(event->GetText()); event->SetText(out_buffer_orig); } else { map = NULL; } kry_free(out_buffer_orig); } if(event->GetType() == kryEvent::EVENT_BLANK) { char *type_str = event->GetTypeString(); char *subtype_str = event->GetTypeSubString(); char *buffer = kry_strdup_printf(KRY_LOC "%s: %s\n", type_str, subtype_str); kry_free(type_str); kry_free(subtype_str); this->WriteString(buffer); kry_free(buffer); } else if(event->GetType() == kryEvent::EVENT_COMMENT_STRING || event->GetType() == kryEvent::EVENT_COMMENT_BLANK) { char *buffer = kry_strdup_printf(KRY_LOC "%s%s\n", event->GetPrefix(), event->GetText()); this->WriteString(buffer); kry_free(buffer); } else { this->WriteRowData(columns, event); } if(orig_text) { event->SetText(orig_text); kry_free(orig_text); } if(this->m_double_space) this->WriteString("\n"); } delete style_iconv_map; } void krySubWriterSSA::WriteRowDataStyles(GList *columns, kryList *styles) { kryListIterator style_iter; styles->GetIterator(&style_iter); while(kryStyle *style = style_iter.GetNext()) { if(style->GetType() == KRY_STYLE_COMMENT) { char *buffer = kry_strdup_printf(KRY_LOC "%s\n", style->GetPrefix()); this->WriteString(buffer); kry_free(buffer); } else { this->WriteRowData(columns, style); } } } void krySubWriterSSA::WriteProperty(char *property, gboolean write_if_empty) { kryScript *script = this->GetScript(); char *val = script->GetProperty(property); if(!val) { if(write_if_empty) val = ""; else return; } char *buffer = kry_strdup_printf(KRY_LOC "%s: %s\n", property, val); this->WriteString(buffer); kry_free(buffer); } void krySubWriterSSA::WriteProperties() { kryScript *script = this->GetScript(); kryListIterator iter; char *prop; script->GetPropertyNameIterator(&iter); while((prop = iter.GetNext())) { if(!strcasecmp(iter.GetData(), "ScriptType") || !strcasecmp(iter.GetData(), "SabbuVersion")) continue; char *key = iter.GetData(); if(key[0] == ':') { char *buffer = kry_strdup_printf(KRY_LOC "%s\n", script->GetProperty(iter.GetData())); this->WriteString(buffer); kry_free(buffer); } else { this->WriteProperty(key); } } } void krySubWriterSSA::WriteStyles(GList *columns) { kryScript *script = this->GetScript(); kryList styles; kryListIterator iter; char *style_name; script->GetStyleNameIterator(&iter); while((style_name = iter.GetNext())) { kryStyle *style = script->GetStyle(style_name); if(style->GetType() != KRY_STYLE_COMMENT && (style->GetStrikethrough() || style->GetUnderline() || style->GetFontAngleZ())) { script->SetType(KRY_FORMAT_ASS); style->SetType(KRY_STYLE_ASS); } styles.Append(style); } sub_ssa_save_allocate_temp(columns, script); this->ComputeColumnWidthsStyles(columns, &styles); this->ComputeColumnFormats(columns); this->WriteColumnHeaders(columns); this->WriteRowDataStyles(columns, &styles); sub_ssa_save_free_temp(columns, script); } void krySubWriterSSA::WriteEvents(GList *columns) { kryScript *script = this->GetScript(); sub_ssa_save_allocate_temp(columns, script); this->ComputeColumnWidthsEvents(columns, &script->GetEventList()); this->ComputeColumnFormats(columns); this->WriteColumnHeaders(columns); this->WriteRowDataEvents(columns); sub_ssa_save_free_temp(columns, script); } krySubWriterSSA::krySubWriterSSA(kryScript *script, char *filename) : krySubWriter(script, filename), m_format_script(FALSE), m_double_space(FALSE), m_format_csv(FALSE) { } void krySubWriterSSA::EnableFormatScript() { this->m_format_script = TRUE; } void krySubWriterSSA::EnableDoubleSpace() { this->m_double_space = TRUE; } void krySubWriterSSA::EnableFormatCSV() { this->m_format_csv = TRUE; } char *krySubWriterSSA::WriteScript() { kryListIterator iter; char *buffer; kryList columns; kryScript *script = this->GetScript(); if(!this->Open()) return FALSE; this->WriteHeaders(); this->WriteString("[Script Info]\n"); this->WriteString("; This is a Sub Station Alpha v4 script.\n"); this->WriteString("; This file was generated using Sabbu,\n"); this->WriteString("; Go to http://www.sabbu.com\n"); this->WriteString("; or e-mail kryptolus@gmail.com\n"); buffer = script->GetType() == KRY_FORMAT_ASS ? (char *) "ScriptType: v4.00+\n" : (char *) "ScriptType: v4.00\n"; this->WriteString(buffer); buffer = kry_strdup_printf(KRY_LOC "SabbuVersion: %s\n", app.version); this->WriteString(buffer); kry_free(buffer); this->WriteProperties(); GList *save_columns; // write styles out { if(script->GetType() == KRY_FORMAT_ASS) save_columns = this->GetColumnListWrite(krySubSSA::COLUMN_LIST_WRITE_STYLE_ASS); else save_columns = this->GetColumnListWrite(krySubSSA::COLUMN_LIST_WRITE_STYLE_SSA); this->WriteString("\n"); buffer = (script->GetType() == KRY_FORMAT_ASS ? (char *) "[V4+ Styles]\n" : (char *) "[V4 Styles]\n"); this->WriteString(buffer); this->WriteStyles(save_columns); this->FreeColumnListWrite(save_columns); } // write events out { if(script->GetType() == KRY_FORMAT_ASS) save_columns = this->GetColumnListWrite(krySubSSA::COLUMN_LIST_WRITE_EVENT_ASS); else save_columns = this->GetColumnListWrite(krySubSSA::COLUMN_LIST_WRITE_EVENT_SSA); this->WriteString("\n"); this->WriteString("[Events]\n"); this->WriteEvents(save_columns); this->FreeColumnListWrite(save_columns); } return NULL; }