| 218 | | ComponentResult LoadSingleTextSubtitle(const FSRef *theDirectory, CFStringRef filename, Movie theMovie, Track *firstSubTrack, int subtitleType) |
|---|
| | 221 | static int parse_SYNC(NSString *str) |
|---|
| | 222 | { |
|---|
| | 223 | NSScanner *sc = [NSScanner scannerWithString:str]; |
|---|
| | 224 | |
|---|
| | 225 | int res; |
|---|
| | 226 | |
|---|
| | 227 | if ([sc scanString:@"START=" intoString:nil]) |
|---|
| | 228 | [sc scanInt:&res]; |
|---|
| | 229 | |
|---|
| | 230 | return res; |
|---|
| | 231 | } |
|---|
| | 232 | |
|---|
| | 233 | static NSArray *parse_STYLE(NSString *str) |
|---|
| | 234 | { |
|---|
| | 235 | NSScanner *sc = [NSScanner scannerWithString:str]; |
|---|
| | 236 | |
|---|
| | 237 | NSString *firstRes; |
|---|
| | 238 | NSString *secondRes; |
|---|
| | 239 | NSArray *subArray; |
|---|
| | 240 | int secondLoc; |
|---|
| | 241 | |
|---|
| | 242 | [sc scanUpToString:@"<P CLASS=" intoString:nil]; |
|---|
| | 243 | if ([sc scanString:@"<P CLASS=" intoString:nil]) |
|---|
| | 244 | [sc scanUpToString:@">" intoString:&firstRes]; |
|---|
| | 245 | else |
|---|
| | 246 | firstRes = @"noClass"; |
|---|
| | 247 | |
|---|
| | 248 | secondLoc = [str length] * .9; |
|---|
| | 249 | [sc setScanLocation:secondLoc]; |
|---|
| | 250 | |
|---|
| | 251 | [sc scanUpToString:@"<P CLASS=" intoString:nil]; |
|---|
| | 252 | if ([sc scanString:@"<P CLASS=" intoString:nil]) |
|---|
| | 253 | [sc scanUpToString:@">" intoString:&secondRes]; |
|---|
| | 254 | else |
|---|
| | 255 | secondRes = @"noClass"; |
|---|
| | 256 | |
|---|
| | 257 | if ([firstRes isEqualToString:secondRes]) |
|---|
| | 258 | secondRes = @"noClass"; |
|---|
| | 259 | |
|---|
| | 260 | subArray = [NSArray arrayWithObjects:firstRes, secondRes, nil]; |
|---|
| | 261 | |
|---|
| | 262 | return subArray; |
|---|
| | 263 | } |
|---|
| | 264 | |
|---|
| | 265 | static int parse_P(NSString *str, NSArray *subArray) |
|---|
| | 266 | { |
|---|
| | 267 | NSScanner *sc = [NSScanner scannerWithString:str]; |
|---|
| | 268 | |
|---|
| | 269 | NSString *res; |
|---|
| | 270 | int subLang; |
|---|
| | 271 | |
|---|
| | 272 | if ([sc scanString:@"CLASS=" intoString:nil]) |
|---|
| | 273 | [sc scanUpToString:@">" intoString:&res]; |
|---|
| | 274 | else |
|---|
| | 275 | res = @"noClass"; |
|---|
| | 276 | |
|---|
| | 277 | if ([res isEqualToString:[subArray objectAtIndex:0]]) |
|---|
| | 278 | subLang = 1; |
|---|
| | 279 | else if ([res isEqualToString:[subArray objectAtIndex:1]]) |
|---|
| | 280 | subLang = 2; |
|---|
| | 281 | else |
|---|
| | 282 | subLang = 3; |
|---|
| | 283 | |
|---|
| | 284 | return subLang; |
|---|
| | 285 | } |
|---|
| | 286 | |
|---|
| | 287 | static NSString *parse_COLOR(NSString *str) |
|---|
| | 288 | { |
|---|
| | 289 | NSString *cvalue; |
|---|
| | 290 | NSMutableString *cname = [NSMutableString stringWithFormat:@"%@", str]; |
|---|
| | 291 | |
|---|
| | 292 | if ([cname characterAtIndex:0] == '#' && [cname lengthOfBytesUsingEncoding:NSASCIIStringEncoding] == 7) |
|---|
| | 293 | cvalue = [NSString stringWithFormat:@"{\\1c&H%@%@%@&}", [cname substringWithRange:NSMakeRange(5,2)], [cname substringWithRange:NSMakeRange(3,2)], [cname substringWithRange:NSMakeRange(1,2)]]; |
|---|
| | 294 | else { |
|---|
| | 295 | [cname replaceOccurrencesOfString:@"Aqua" withString:@"00FFFF" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 296 | [cname replaceOccurrencesOfString:@"Black" withString:@"000000" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 297 | [cname replaceOccurrencesOfString:@"Blue" withString:@"0000FF" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 298 | [cname replaceOccurrencesOfString:@"Fuchsia" withString:@"FF00FF" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 299 | [cname replaceOccurrencesOfString:@"Gray" withString:@"808080" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 300 | [cname replaceOccurrencesOfString:@"Green" withString:@"008000" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 301 | [cname replaceOccurrencesOfString:@"Lime" withString:@"00FF00" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 302 | [cname replaceOccurrencesOfString:@"Maroon" withString:@"800000" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 303 | [cname replaceOccurrencesOfString:@"Navy" withString:@"000080" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 304 | [cname replaceOccurrencesOfString:@"Olive" withString:@"808000" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 305 | [cname replaceOccurrencesOfString:@"Purple" withString:@"800080" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 306 | [cname replaceOccurrencesOfString:@"Red" withString:@"FF0000" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 307 | [cname replaceOccurrencesOfString:@"Silver" withString:@"C0C0C0" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 308 | [cname replaceOccurrencesOfString:@"Teal" withString:@"008080" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 309 | [cname replaceOccurrencesOfString:@"White" withString:@"FFFFFF" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 310 | [cname replaceOccurrencesOfString:@"Yellow" withString:@"FFFF00" options:1 range:NSMakeRange(0,[cname length])]; |
|---|
| | 311 | |
|---|
| | 312 | if ([cname lengthOfBytesUsingEncoding:NSASCIIStringEncoding] == 6) |
|---|
| | 313 | cvalue = [NSString stringWithFormat:@"{\\1c&H%@%@%@&}", [cname substringWithRange:NSMakeRange(4,2)], [cname substringWithRange:NSMakeRange(2,2)], [cname substringWithRange:NSMakeRange(0,2)]]; |
|---|
| | 314 | else |
|---|
| | 315 | cvalue = @"{\\1c&HFFFFFF&}"; |
|---|
| | 316 | } |
|---|
| | 317 | |
|---|
| | 318 | return cvalue; |
|---|
| | 319 | } |
|---|
| | 320 | |
|---|
| | 321 | static NSString *parse_FONT(NSString *str) |
|---|
| | 322 | { |
|---|
| | 323 | NSScanner *sc = [NSScanner scannerWithString:str]; |
|---|
| | 324 | |
|---|
| | 325 | NSString *res; |
|---|
| | 326 | NSString *color; |
|---|
| | 327 | |
|---|
| | 328 | if ([sc scanString:@"COLOR=" intoString:nil]) { |
|---|
| | 329 | [sc scanUpToString:@">" intoString:&res]; |
|---|
| | 330 | color = parse_COLOR(res); |
|---|
| | 331 | } |
|---|
| | 332 | else |
|---|
| | 333 | color = @"{\\1c&HFFFFFF&}"; |
|---|
| | 334 | |
|---|
| | 335 | return color; |
|---|
| | 336 | } |
|---|
| | 337 | |
|---|
| | 338 | static NSMutableString *StandardizeSMIWhitespace(NSString *str) |
|---|
| | 339 | { |
|---|
| | 340 | if (!str) return nil; |
|---|
| | 341 | NSMutableString *ms = [NSMutableString stringWithString:str]; |
|---|
| | 342 | [ms replaceOccurrencesOfString:@"\r" withString:@"" options:0 range:NSMakeRange(0,[ms length])]; |
|---|
| | 343 | [ms replaceOccurrencesOfString:@"\n" withString:@"" options:0 range:NSMakeRange(0,[ms length])]; |
|---|
| | 344 | [ms replaceOccurrencesOfString:@" " withString:@" " options:0 range:NSMakeRange(0,[ms length])]; |
|---|
| | 345 | return ms; |
|---|
| | 346 | } |
|---|
| | 347 | |
|---|
| | 348 | static void LoadSMIFromPath(NSString *path, SubSerializer *ss, int subCount) |
|---|
| | 349 | { |
|---|
| | 350 | NSMutableString *smi = StandardizeSMIWhitespace(STLoadFileWithUnknownEncoding(path)); |
|---|
| | 351 | if (!smi) return; |
|---|
| | 352 | |
|---|
| | 353 | NSScanner *sc = [NSScanner scannerWithString:smi]; |
|---|
| | 354 | NSString *res = nil; |
|---|
| | 355 | [sc setCharactersToBeSkipped:nil]; |
|---|
| | 356 | [sc setCaseSensitive:NO]; |
|---|
| | 357 | |
|---|
| | 358 | NSMutableString *cmt = [NSMutableString stringWithFormat:@""]; |
|---|
| | 359 | NSArray *subLanguage = parse_STYLE(smi); |
|---|
| | 360 | |
|---|
| | 361 | int startTime=-1, endTime=-1, syncTime=-1; |
|---|
| | 362 | int cc=1; |
|---|
| | 363 | |
|---|
| | 364 | enum { |
|---|
| | 365 | TAG_INIT, |
|---|
| | 366 | TAG_SYNC, |
|---|
| | 367 | TAG_P, |
|---|
| | 368 | TAG_BR_OPEN, |
|---|
| | 369 | TAG_BR_CLOSE, |
|---|
| | 370 | TAG_B_OPEN, |
|---|
| | 371 | TAG_B_CLOSE, |
|---|
| | 372 | TAG_I_OPEN, |
|---|
| | 373 | TAG_I_CLOSE, |
|---|
| | 374 | TAG_FONT_OPEN, |
|---|
| | 375 | TAG_FONT_CLOSE, |
|---|
| | 376 | TAG_COMMENT |
|---|
| | 377 | } state = TAG_INIT; |
|---|
| | 378 | |
|---|
| | 379 | do { |
|---|
| | 380 | switch (state) { |
|---|
| | 381 | case TAG_INIT: |
|---|
| | 382 | [sc scanUpToString:@"<SYNC" intoString:nil]; |
|---|
| | 383 | if ([sc scanString:@"<SYNC" intoString:nil]) |
|---|
| | 384 | state = TAG_SYNC; |
|---|
| | 385 | break; |
|---|
| | 386 | case TAG_SYNC: |
|---|
| | 387 | [sc scanUpToString:@">" intoString:&res]; |
|---|
| | 388 | syncTime = parse_SYNC(res); |
|---|
| | 389 | if (startTime > -1) { |
|---|
| | 390 | endTime = syncTime; |
|---|
| | 391 | if (subCount == 2 && cc == 2) |
|---|
| | 392 | [cmt insertString:@"{\\an8}" atIndex:0]; |
|---|
| | 393 | if (subCount == 1 && cc == 1 || subCount == 2 && cc == 2) { |
|---|
| | 394 | SubLine *sl = [[SubLine alloc] initWithLine:cmt start:startTime end:endTime]; |
|---|
| | 395 | [ss addLine:[sl autorelease]]; |
|---|
| | 396 | } |
|---|
| | 397 | } |
|---|
| | 398 | startTime = syncTime; |
|---|
| | 399 | [cmt setString:@""]; |
|---|
| | 400 | state = TAG_COMMENT; |
|---|
| | 401 | break; |
|---|
| | 402 | case TAG_P: |
|---|
| | 403 | [sc scanUpToString:@">" intoString:&res]; |
|---|
| | 404 | cc = parse_P(res, subLanguage); |
|---|
| | 405 | [cmt setString:@""]; |
|---|
| | 406 | state = TAG_COMMENT; |
|---|
| | 407 | break; |
|---|
| | 408 | case TAG_BR_OPEN: |
|---|
| | 409 | [sc scanUpToString:@">" intoString:nil]; |
|---|
| | 410 | [cmt appendString:@"\\n"]; |
|---|
| | 411 | state = TAG_COMMENT; |
|---|
| | 412 | break; |
|---|
| | 413 | case TAG_BR_CLOSE: |
|---|
| | 414 | [sc scanUpToString:@">" intoString:nil]; |
|---|
| | 415 | [cmt appendString:@"\\n"]; |
|---|
| | 416 | state = TAG_COMMENT; |
|---|
| | 417 | break; |
|---|
| | 418 | case TAG_B_OPEN: |
|---|
| | 419 | [sc scanUpToString:@">" intoString:&res]; |
|---|
| | 420 | [cmt appendString:@"{\\b1}"]; |
|---|
| | 421 | state = TAG_COMMENT; |
|---|
| | 422 | break; |
|---|
| | 423 | case TAG_B_CLOSE: |
|---|
| | 424 | [sc scanUpToString:@">" intoString:nil]; |
|---|
| | 425 | [cmt appendString:@"{\\b0}"]; |
|---|
| | 426 | state = TAG_COMMENT; |
|---|
| | 427 | break; |
|---|
| | 428 | case TAG_I_OPEN: |
|---|
| | 429 | [sc scanUpToString:@">" intoString:&res]; |
|---|
| | 430 | [cmt appendString:@"{\\i1}"]; |
|---|
| | 431 | state = TAG_COMMENT; |
|---|
| | 432 | break; |
|---|
| | 433 | case TAG_I_CLOSE: |
|---|
| | 434 | [sc scanUpToString:@">" intoString:nil]; |
|---|
| | 435 | [cmt appendString:@"{\\i0}"]; |
|---|
| | 436 | state = TAG_COMMENT; |
|---|
| | 437 | break; |
|---|
| | 438 | case TAG_FONT_OPEN: |
|---|
| | 439 | [sc scanUpToString:@">" intoString:&res]; |
|---|
| | 440 | [cmt appendString:parse_FONT(res)]; |
|---|
| | 441 | state = TAG_COMMENT; |
|---|
| | 442 | break; |
|---|
| | 443 | case TAG_FONT_CLOSE: |
|---|
| | 444 | [sc scanUpToString:@">" intoString:nil]; |
|---|
| | 445 | [cmt appendString:@"{\\1c&HFFFFFF&}"]; |
|---|
| | 446 | state = TAG_COMMENT; |
|---|
| | 447 | break; |
|---|
| | 448 | case TAG_COMMENT: |
|---|
| | 449 | [sc scanString:@">" intoString:nil]; |
|---|
| | 450 | if ([sc scanUpToString:@"<" intoString:&res]) |
|---|
| | 451 | [cmt appendString:res]; |
|---|
| | 452 | else |
|---|
| | 453 | [cmt appendString:@"<>"]; |
|---|
| | 454 | if ([sc scanString:@"<" intoString:nil]) { |
|---|
| | 455 | if ([sc scanString:@"SYNC" intoString:nil]) { |
|---|
| | 456 | state = TAG_SYNC; |
|---|
| | 457 | break; |
|---|
| | 458 | } |
|---|
| | 459 | else if ([sc scanString:@"P" intoString:nil]) { |
|---|
| | 460 | state = TAG_P; |
|---|
| | 461 | break; |
|---|
| | 462 | } |
|---|
| | 463 | else if ([sc scanString:@"BR" intoString:nil]) { |
|---|
| | 464 | state = TAG_BR_OPEN; |
|---|
| | 465 | break; |
|---|
| | 466 | } |
|---|
| | 467 | else if ([sc scanString:@"/BR" intoString:nil]) { |
|---|
| | 468 | state = TAG_BR_CLOSE; |
|---|
| | 469 | break; |
|---|
| | 470 | } |
|---|
| | 471 | else if ([sc scanString:@"B" intoString:nil]) { |
|---|
| | 472 | state = TAG_B_OPEN; |
|---|
| | 473 | break; |
|---|
| | 474 | } |
|---|
| | 475 | else if ([sc scanString:@"/B" intoString:nil]) { |
|---|
| | 476 | state = TAG_B_CLOSE; |
|---|
| | 477 | break; |
|---|
| | 478 | } |
|---|
| | 479 | else if ([sc scanString:@"I" intoString:nil]) { |
|---|
| | 480 | state = TAG_I_OPEN; |
|---|
| | 481 | break; |
|---|
| | 482 | } |
|---|
| | 483 | else if ([sc scanString:@"/I" intoString:nil]) { |
|---|
| | 484 | state = TAG_I_CLOSE; |
|---|
| | 485 | break; |
|---|
| | 486 | } |
|---|
| | 487 | else if ([sc scanString:@"FONT" intoString:nil]) { |
|---|
| | 488 | state = TAG_FONT_OPEN; |
|---|
| | 489 | break; |
|---|
| | 490 | } |
|---|
| | 491 | else if ([sc scanString:@"/FONT" intoString:nil]) { |
|---|
| | 492 | state = TAG_FONT_CLOSE; |
|---|
| | 493 | break; |
|---|
| | 494 | } |
|---|
| | 495 | else { |
|---|
| | 496 | [cmt appendString:@"<"]; |
|---|
| | 497 | state = TAG_COMMENT; |
|---|
| | 498 | break; |
|---|
| | 499 | } |
|---|
| | 500 | } |
|---|
| | 501 | } |
|---|
| | 502 | } while (![sc isAtEnd]); |
|---|
| | 503 | } |
|---|
| | 504 | |
|---|
| | 505 | static ComponentResult LoadSingleTextSubtitle(const FSRef *theDirectory, CFStringRef filename, Movie theMovie, Track *firstSubTrack, int subtitleType, int whichTrack) |
|---|