// vi: et sw=2 nu
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define EXTENSION ".srt" // default extension for output
#define BODY "<BODY>"
#define BUFFER 65535
#define SYNCTAG "<SYNC START="
char *change_extension(char *in, char *out);
char *convert_timestamp(long int, char *);
char *copy_without_space(char *, char *);
char *remove_br_tag(char *);
char *remove_tags(char *);
char *wrap_line(char *);
int error(int); // message from table
int is_not_empty(char *);
void check_commandline_arg(char *);
FILE *in_file;
FILE *out_file;
char *buffer;
char *found_sync_tag, *found_new_sync_tag, *alt_buffer;
char *inputline;
char *outputline;
char time_b[32];
char time_e[32];
long int time_begin = 0;
long int time_end = 0;
unsigned int STRING_LENGTH = 70;
unsigned int linenm = 1;
unsigned int printable = 0;
int main(int argc, char *argv[]) {
int flag = 0, flag2 = 0;
char *in_filename = (char *)malloc(255);
char *out_filename = (char *)malloc(255);
strcpy(out_filename, "CON");
printf("\nConvert SMI to SRT...\n");
if (argc == 1) { // no param
error(0);
return 0;
} else {
strcpy(in_filename, argv[1]);
if (argc > 2) {
strcpy(out_filename, argv[2]);
} else {
change_extension(in_filename, out_filename);
}
if (argc > 3)
check_commandline_arg(argv[3]);
}
in_file = fopen(in_filename, "r");
out_file = fopen(out_filename, "w");
if (!(size_t)out_file)
return error(2);
if (!(size_t)in_file)
return error(1);
if (!(buffer = (char *)malloc(BUFFER)))
return error(3);
if (!(inputline = (char *)malloc(BUFFER)) || (size_t)inputline == -1)
return error(3);
if (!(outputline = (char *)malloc(BUFFER)) || (size_t)outputline == -1)
return error(3);
if (!(alt_buffer = (char *)malloc(BUFFER)) || (size_t)alt_buffer == -1)
return error(3);
printf("Converting from %s to %s\n", in_filename, out_filename);
while (1) {
// <BODY> 태그를 찾을 때까지 각 행을 읽어서 진행하고
// 파일에서 못찾으면 종료
// `fgets`는 최대 버퍼 길이만큼 읽거나, 개행문자를 만날때까지 읽음
if (!fgets(buffer, BUFFER, in_file))
return error(3);
strupr(buffer);
if (strstr(buffer, BODY))
break;
}
// MAIN LOOP
flag = 0;
while (!flag) {
if (flag2) {
flag2 = 0;
strcpy(buffer, alt_buffer);
} else {
// 만약 파일의 끝이었다면 종료
if (!fgets(buffer, BUFFER, in_file)) {
break;
}
}
*inputline = '\0';
strcpy(inputline, buffer);
strupr(buffer);
found_sync_tag = strstr(buffer, SYNCTAG);
if (!found_sync_tag)
continue;
while (!flag2) {
if (!fgets(buffer, BUFFER, in_file)) {
flag = 1;
break;
} // EOF
// 만약 또 SYNC 태그가 발견됐다면 inputline에 읽었던 라인을 추가
// 즉 새로운 SYNC 태그를 발견하기 전까지 계속 inputline에 새 라인을 추가
strcpy(alt_buffer, buffer);
strupr(buffer);
found_new_sync_tag = strstr(buffer, SYNCTAG);
if (!found_new_sync_tag)
strcat(inputline, alt_buffer);
else
flag2 = 1; // END OF SUB. LINE FOUND
}
// 태그 처리를 위해 1건의 대사 정보가 연결된 inputline을 buffer로 다시
// 복사.
strcpy(buffer, inputline);
strupr(buffer);
found_new_sync_tag = strstr(buffer, SYNCTAG);
sscanf(found_new_sync_tag, "<SYNC START=%d", &time_begin);
if (printable) {
strcpy(time_e, time_b);
convert_timestamp(time_begin, time_b);
time_end = time_begin;
fprintf(out_file, "%d\n", linenm++);
fprintf(out_file, "%s --> %s\n", time_e, time_b);
fprintf(out_file, "%s\n", outputline);
} else {
convert_timestamp(time_begin, time_b);
strcpy(time_e, time_b);
time_end = time_begin;
}
// LINE CONVERTING
remove_br_tag(inputline);
remove_tags(inputline);
copy_without_space(inputline, outputline);
wrap_line(outputline);
printable = is_not_empty(outputline);
}
fclose(in_file);
fclose(out_file);
free(buffer);
free(inputline);
free(outputline);
free(in_filename);
free(out_filename);
printf("converted a %d lines \n\n", linenm - 1);
return 0;
}
int error(int code) {
const char errors[][80] = {"use: smi2srt.exe filename.smi [filename.srt] "
"[-170] More see in readme.txe",
"Unable to open input file", "Memory too low",
"<body> not found"};
printf("Error %d (%s)", code, errors[code]);
return code;
}
char *remove_br_tag(char *line) {
char *temp = strstr(line, "<br>");
while (temp) {
*temp = '\n';
*(temp + 1) = ' ';
*(temp + 2) = ' ';
*(temp + 3) = ' ';
temp = strstr(temp, "<br>");
}
return line;
}
char *remove_tags(char *line) {
char *temp = line;
while (*temp) {
if (*temp == '<') {
while (temp) {
if (*temp == '>') {
*temp = ' ';
break;
}
*(temp++) = ' ';
}
}
if (*temp == '&') {
while (*temp) {
if (*(temp + 1) == ' ') {
temp++;
break;
}
if (*(temp + 2) == ';') {
*temp = ' ';
break;
}
*(temp++) = ' ';
}
} else
temp++;
}
return line;
}
char *copy_without_space(char *in, char *out) {
// in -> out으로 문자열을 복사하면서, 연속된 공백은 한 개의 공백으로 변경한다.
int sp = 1;
while (*in) {
if (*in == ' ' && sp == 1) {
in++;
continue;
}
if (*in == ' ' && sp == 0)
sp = 1;
else
sp = 0;
*(out++) = *(in++);
}
*out = '\0';
return out;
}
char *convert_timestamp(long int number, char *line) {
// ms값을 hh:mm:ss.zzz의 형식으로 변환한다.
long int q, r;
char vals[32][4];
q = number / 3600000;
r = number % 3600000;
sprintf(vals[0], "%02d", q);
q = r / 60000;
r = r % 60000;
sprintf(vals[1], "%02d", q);
q = r / 1000;
r = r % 1000;
sprintf(vals[2], "%2d", q);
sprintf(vals[3], "%03d", r);
sprintf(line, "%s:%s:%s.%s", vals[0], vals[1], vals[2], vals[3]);
return line;
}
char *wrap_line(char *line) {
// line이 미리 정해진 STRING_LENGTH 보다 크면
// 그 앞 공백에서 줄바꿈한다.
// 줄바꿈하면서 연속된 개행문자가 있으면 공백으로 치환한다.
size_t i = 0;
int old = 0;
int flag = 0;
while (1) {
old = i;
i = i + STRING_LENGTH;
if (strlen(line) <= i)
break;
while (i > old) {
if (*(line + i) == ' ') {
*(line + i) = '\n';
break;
}
if (*line == '\n')
break;
i--;
}
}
// REMOVE DUPLICATED "\n"
flag = 0;
if (*line == '\n') {
flag = 1;
*line = ' ';
}
while (*line) {
if (*line == '\n') {
if (flag)
*line = ' ';
else
flag = 1;
} else if (*line != ' ')
flag = 0;
line++;
}
return line;
}
int is_not_empty(char *line) {
// 문자열에 공백만 있는지 판단한다.
// 공백만 있는 대사라인이라면 출력하지 않는다.
while (*line) {
if (*line != ' ' && *line != '\n')
return 1;
line++;
}
return 0;
}
void check_commandline_arg(char *line) {
// 명령줄 인자에 -l 옵션이 있는지 검사한다.
sscanf(line, "-l%d", &STRING_LENGTH);
}
char *change_extension(char *in, char *out) {
// in -> out 으로 문자열을 복사하면서,
// 확장자를 .srt로 변경해준다.
char *temp;
strcpy(out, in);
temp = out + strlen(out);
while (temp != out) {
if (*temp == '.') {
*temp = '\0';
break;
}
temp--;
}
strcat(out, EXTENSION);
return out;
}
관련