Changeset 872

Show
Ignore:
Timestamp:
05/04/08 19:42:27 (4 months ago)
Author:
astrange
Message:

SAMI subtitle importing.
Patch by capri91 (gmail com).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/Subtitles/SubContext.h

    r838 r872  
    1010#import "SubRenderer.h" 
    1111 
    12 enum {kSubTypeSSA, kSubTypeASS, kSubTypeSRT}; 
     12enum {kSubTypeSSA, kSubTypeASS, kSubTypeSRT, kSubTypeSMI}; 
    1313enum {kSubCollisionsNormal, kSubCollisionsReverse}; 
    1414enum {kSubLineWrapTopWider = 0, kSubLineWrapSimple, kSubLineWrapNone, kSubLineWrapBottomWider}; 
  • trunk/Subtitles/SubImport.mm

    r850 r872  
    1010#include "CommonUtils.h" 
    1111#include "Codecprintf.h" 
     12#include "CodecIDs.h" 
    1213#import "SubImport.h" 
    1314#import "SubParsing.h" 
     
    161162        return [nssSub substringToIndex:[nssSub rangeOfString:@"[Events]" options:NSLiteralSearch].location]; 
    162163} 
     164 
     165#pragma mark SAMI Parsing 
    163166 
    164167static void LoadSRTFromPath(NSString *path, SubSerializer *ss) 
     
    216219} 
    217220 
    218 ComponentResult LoadSingleTextSubtitle(const FSRef *theDirectory, CFStringRef filename, Movie theMovie, Track *firstSubTrack, int subtitleType) 
     221static 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 
     233static 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 
     265static 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 
     287static 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 
     321static 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 
     338static 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:@"&nbsp;" withString:@" " options:0 range:NSMakeRange(0,[ms length])]; 
     345        return ms; 
     346
     347 
     348static 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 
     505static ComponentResult LoadSingleTextSubtitle(const FSRef *theDirectory, CFStringRef filename, Movie theMovie, Track *firstSubTrack, int subtitleType, int whichTrack) 
    219506{ 
    220507        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
     
    237524                { 
    238525                        timeBase = 100; 
    239                         subCodec = 'SSA '
     526                        subCodec = kSubFormatSSA
    240527                        const char *cheader = [LoadSSAFromPath(nsPath, ss) UTF8String]; 
    241528                        int headerLen = strlen(cheader); 
     
    245532                case kSubTypeSRT: 
    246533                        timeBase = 1000; 
    247                         subCodec = 'SRT '
     534                        subCodec = kSubFormatUTF8
    248535                        LoadSRTFromPath(nsPath, ss); 
     536                        break; 
     537                case kSubTypeSMI: 
     538                        timeBase = 1000; 
     539                        subCodec = kSubFormatUTF8; 
     540                        LoadSMIFromPath(nsPath, ss, whichTrack); 
    249541                        break; 
    250542        } 
     
    418710                                                if (actRange.length && actRange.location == extRange.location) 
    419711                                                        err = LoadVobSubSubtitles(&parentDir, cfFoundFilename, theMovie, &firstSubTrack); 
     712                                                else { 
     713                                                        actRange = CFStringFind(cfFoundFilename, CFSTR(".smi"), kCFCompareCaseInsensitive | kCFCompareBackwards); 
     714                                                        if (actRange.length && actRange.location == extRange.location) 
     715                                                                subType = kSubTypeSMI; 
     716                                                } 
    420717                                        } 
    421718                                } 
    422719                        } 
    423720                         
    424                         if (subType != -1) err = LoadSingleTextSubtitle(&parentDir, cfFoundFilename, theMovie, &firstSubTrack, subType); 
    425                          
     721                        if (subType == kSubTypeSMI) { 
     722                                err = LoadSingleTextSubtitle(&parentDir, cfFoundFilename, theMovie, &firstSubTrack, kSubTypeSMI, 1); 
     723                                if (!err) err = LoadSingleTextSubtitle(&parentDir, cfFoundFilename, theMovie, &firstSubTrack, kSubTypeSMI, 2); 
     724                        } 
     725                        else if (subType != -1) 
     726                                err = LoadSingleTextSubtitle(&parentDir, cfFoundFilename, theMovie, &firstSubTrack, subType, 0); 
     727 
    426728                        if (err) goto bail; 
    427729                }