콘텐츠로 건너뛰기
Home » [c/c++] SMI를 SRT로 컨버팅하기

[c/c++] SMI를 SRT로 컨버팅하기

예전에 올렸던 George Shuklin의 소스를 약간 수정하여 정리함. mingw64를 사용하여 64비트 플랫폼에서 컴파일 할 수 있다.

// 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;
}
태그: